2020-07-19--scrapy框架2
scrapy调试
通常,运行scrapy爬虫的方式是在命令行输入scrapy crawl ,调试的常用方式是在命令行输入scrapy shell 。总的来说,调试方法比较单一。
其实,还有两种调试方法,可以在pycharm中实现调试。
1.使用scrapy.cmdline的execute方法 首先,在项目文件scrapy.cfg的同级建立main.py文件(注意,必须是同级建立),在其中键入如下代码:
from scrapy.cmdline import execute
execute('scrapy crawl blog'.split())
# 或
# execute(['scrapy', 'crawl', 'spider_name']) spider_name替换为你自己的爬虫名称
Spiders
Spider类定义了如何爬取某个网站。包括了爬取的动作(例如:是否跟进链接)以及如何从网页的内容中提取结构化数据(爬取item)。简而言之,Spider就是你定义爬取的动作及分析某个网页(或者是有些网页)的地方。
对spider来说,爬取的循环类似如下:
以初始的URL初始化Request,并设置回调函数。当该request下载完毕并返回时,将生成response,并作为参数传给该回调函数。spider中初始的request是通过调用start_requests() 来获取。start_requests() 读取start_urls中的URL,并以parse为回调函数生成 Request。
在回调函数内分析返回的(网页)内容,返回 Item 对象、dict、 Request 或者一个包括三者的可迭代容器。 返回的Request对象之后会经过Scrapy处理,下载相应的内容,并调用设置的callback函数(函数可相同)。
在回调函数内,您可以使用 选择器(Selectors) (您也可以使用BeautifulSoup, lxml 或者您想用的任何解析器) 来分析网页内容,并根据分析的数据生成item。
最后,由spider返回的item将被存到数据库(由某些 Item Pipeline 处理)
Scrapy爬取博客园精华区内容
之前我们都是爬取了一个页面,这次爬取多个页面
编写items.py文件 定义需要爬取的内容。
class CnblogsItem(scrapy.Item):
# define the fields for your item here like:
post_author = scrapy.Field() #发布作者
author_link = scrapy.Field() #作者博客主页链接
post_date = scrapy.Field() #发布时间
digg_num = scrapy.Field() #推荐数
title = scrapy.Field() #标题
title_link = scrapy.Field() #标题链接
item_summary = scrapy.Field() #摘要
comment_num = scrapy.Field() #评论数
view_num = scrapy.Field() #阅读数
在spiders/blog.py中编写爬虫:
def parse(self, response:HtmlResponse):
# BlogItem
# 获取文章对象列表
blist = response.xpath('//div[@id="post_list"]/div[@class="post_item"]')
print(blist)
for it in blist:
blog = CnblogsItem()
#作者
auth = it.xpath('.//div[@class="post_item_body"]/div[1]/a/text()').extract_first()
#作者blog主页连接
auth_url = it.xpath('.//div[@class="post_item_body"]/div[1]/a/@href').extract_first()
#发布时间
time = it.xpath('.//div[@class="post_item_body"]/div[1]/text()').extract()
# 推荐数
digg_num = it.xpath('./div[1]/div[1]/span/text()').extract_first()
# 标题
title = it.xpath('.//div[@class="post_item_body"]//h3/a/text()').extract_first()
# 标题链接
title_link = it.xpath('.//div[@class="post_item_body"]//h3/a/@href').extract_first()
# 摘要
item_summary = it.xpath('.//div[@class="post_item_body"]/p/text()').extract_first()
# 评论数
comment_num = it.xpath('.//div[@class="post_item_body"]/div[1]/span[1]/a/text()').extract_first()
# 阅读数
view_num = it.xpath('.//div[@class="post_item_body"]/div[1]/span[2]/a/text()').extract_first()
print(auth,auth_url,time,digg_num,title,title_link,item_summary,comment_num,view_num)
blog['post_author'] = auth
blog['author_link'] = auth_url
blog['post_date'] = time
blog['digg_num'] = digg_num
blog['title'] = title
blog['title_link'] = title_link
blog['item_summary'] = item_summary
blog['comment_num'] = comment_num
blog['view_num'] = view_num
yield blog
#获取下一页链接
#a[text()="Next >"]:获取文本内容为Next >的a标签
next = response.xpath('//a[text()="Next >"]/@href').extract_first()
# print(next)
if next:
next_url = f'http://www.cnblogs.com{next}'
yield scrapy.Request(
next_url,
callback=self.parse
)
分析:
- 首先获取第一个url,进入parse函数,传进来的response是当前页的响应。
- 根据xpath语法获取每个列表项的对象,返回一个列表blist。
- 遍历该列表,分别获取每一项的元素对象it,在it的基础上再进行解析,分别获取作者,作者链接,发布时间。推荐数,标题,标题链接,摘要,评论数,阅读数的对象,使用extract()/extract_first()转为单个对象/列表。
4.创建item数据包,将这些数据加入item数据对象中,yield发送给引擎-->管道文件。
到此第一页的数据爬取完成。
5.利用response解析到Next按钮的链接(也就是下一页的url)
6.判断该url是否存在,如果存在,yield给调度器一个scrapy下的Request()对象,该对象传递两个值,下一个url和回调函数名,进行下一次的爬取。
当管道文件下有多个类时需要判断类型item与需要获取的类型是否一致。
CrawlSpider
class scrapy.spiders.CrawlSpider 在爬取一些特殊类型的网站时,比如一些博客类网站,其网页的链接都会有一些特殊的形式
http://www.cnblogs.com/book/7357421.html
http://www.cnblogs.com/book/7356835.html
比如我们想爬取博客链接,会发现,除了最后的几个数字不同,其他的内容是相同的,因此,我们就可以通过这个类来进行自动抓取相似的链接,而无需我们自己定义。
CrawlSpider类定义了如下的属性和方法:
-
rules 一个包含一个(或多个) Rule 对象的集合(list)。 每个 Rule 对爬取网站的动作定义了特定表现。 Rule对象在下边会介绍。 如果多个rule匹配了相同的链接,则根据他们在本属性中被定义的顺序,第一个会被使用。
-
CrawlSpider需要配合scrapy.spiders.Rule类来实现定义规则 下面介绍scrapy.spiders.Rule类 class scrapy.spiders.Rule(link_extractor, callback=None, cb_kwargs=None, follow=None, process_links=None, process_request=None)
-
link_extractor 这是一个 Link Extractor 对象。 其定义了如何从爬取到的页面提取链接。
-
callback 这是一个callable或string(该spider中同名的函数将会被调用)。 从link_extractor中每获取到链接时将会调用该函数。该回调函数接受一个response作为其第一个参数, 并返回一个包含 Item 以及(或) Request 对象(或者这两者的子类)的列表(list) cb_kwargs 包含传递给回调函数的参数(keyword argument)的字典。 follow 是一个布尔(boolean)值,指定了根据该规则从response提取的链接是否需要跟进。 如果 callback 为None, follow 默认设置为 True ,否则默认为 False 。 process_links 是一个callable或string(该spider中同名的函数将会被调用)。 从link_extractor中获取到链接列表时将会调用该函数。该方法主要用来过滤。 process_request 是一个callable或string(该spider中同名的函数将会被调用)。 该规则提取到每个request时都会调用该函数。该函数必须返回一个request或者None。 (用来过滤request)
CrawlSpider爬取读书网
爬取网页https://www.dushu.com/book/1163_2.html:
上边的2就是页数。
一、开始一个读书网项目
scrapy startproject 项目名称
cd 项目名称/项目名称/spiders
scrapy genspider -t crawl 爬虫名称 域名
scrapy startproject dushu #创建dushu项目
cd dushu/dushu/spiders #进入项目下的spiders目录下
scrapy genspider -t crawl ds www.dushu.com #创建CrawlSpider爬虫文件
使用pycharm打开项目,项目结构与上一章结构一致,但是文件中内容不同。
二、链接提取规则
class DsSpider(CrawlSpider):
name = 'ds'
allowed_domains = ['www.dushu.com']
start_urls = ['https://www.dushu.com/book/1163_1.html']
rules = (
Rule(LinkExtractor(allow=r'/book/1163_\d+.html'), callback='parse_item', follow=True),
)
起始url为https://www.dushu.com/book/1163_1.html,当运行爬取时,会自动匹配规则中allow的正则表达式,不断地匹配第二页,第三页的url,并且回调parse_item(),实现多页爬取。
修改parse_item方法用于解析数据:
def parse_item(self, response:HtmlResponse):
# 获取每个书的元素,返回元素对象列表
blist = response.xpath('//div[@class="bookslist"]/ul/li')
# print(blist)
for it in blist:
book = DushuItem()
# 从当前it元素位置出发查询下属图片
img = it.xpath('./div/div/a/img[1]')
# 获取书名
title = img.xpath('./@alt').extract_first()
# 获取书的链接
src = img.xpath('./@data-original').extract_first()
# 获取书的作者
auth = it.xpath('./div/p[1]/text()').extract_first()
# print(title,src,auth)
book['title'] = title
book['src'] = src
book['auth'] = auth
yield book
四、修改pipelines.py文件用于写入数据
class DushuPipeline:
def open_spider(self, spider):
self.fp = open('dushu.json', 'wb')
self.exporter = JsonLinesItemExporter(self.fp, ensure_ascii=False, encoding='utf-8')
self.fp.write(b"[")
def process_item(self, item, spider):
# self.fp.write(str(item)+"\n")
self.exporter.export_item(item)
self.fp.write(b",")
return item
def close_spider(self, spider):
self.fp.write(b"]")
self.fp.close()
items.py
import scrapy
class DushuItem(scrapy.Item):
# define the fields for your item here like:
src = scrapy.Field() # 发布作者
alt = scrapy.Field() # 作者博客主页链接
author = scrapy.Field() # 发布时间
记得将settings.py中的管道和用户代理和守则修改了。
运行即可。