文章目录
CrawlSpider
- 在上一个爬虫案例中,我们是自己在解析完整个页面后获取下一页的 url,然后重新发送一个请求,有时候我们想要这样做:只要满足某个条件的 url,都进行爬取。这时候我们就可以通过 CrawlSpider 来完成
- CrawlSpider 继承自 Spider,不过是在之前的基础上增加新功能,可以定义爬取的 url 规则,以后 scrapy 碰到满足条件的 url 都进行爬取,而不用手动的 yield Request()
创建 CrawlSpider 爬虫
- 之前创建的方式是通过
scrapy genspider [爬虫名字] [域名]
的方式创建的,如果想要创建 CrawlSpider 爬虫,要用下面命令创建
scrapy genspider -t crawl [爬虫名字] [域名]
LinkExtractors 链接提取器
- 使用 LinkExtractors 可以不用程序员自己提取想要的 url,然后发送请求,这些工作都可以交给 LinkExtractors,他会在所有爬的页面中找到满足规则的 url,实现自动爬取
- 主要参数:
- allow:允许的 url,所有满足这个正则表达式的 url 都会被提取
- deny:禁止的 url,所有满足这个正则表达式的 url 都不会被提取
- allow_domains:允许的域名,只有在这个里面指定的域名的 url 才会被提取
- deny_domains:禁止的域名,所有在这个里面指定的域名的 url 都不会被提取
- restrict_xpaths:严格的 xpath 和 allow 共同过滤链接
Rule 规则类
- 定义爬虫的规则类,以下对这个类做一个简单的介绍
- 主要参数:
- link_extractor:一个 LinkExtractor 对象,用于定义爬取规则
- callback:满足这个规则的 url,应该要执行哪个回调函数,因为 CrawlSpider 使用了 parse 作为回调函数,因此不要覆盖 parse 作为回调函数自己的回调函数
- follow:指定根据该规则从 response 中提取的链接是否需要跟进
- process_links:从 link_extractor 中获取到链接后会传递给这个函数,用来过滤不需要爬取的链接
用 CrawlSpider 爬取 小程序社区
settings.py
...
# 下载延迟
DOWNLOAD_DELAY = random.randint(1,3)
...
DEFAULT_REQUEST_HEADERS = {
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Language': 'en',
'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 \
Safari/537.36'
}
...
# 为了 pipelines.py 能够运行,需要将 ITEM_PIPELINES 取消注释
ITEM_PIPELINES = {
# 300 表示优先级,值越小,优先级越高
'test_spider.pipelines.TestSpiderPipeline': 300,
}
...
xcxsq.py
import scrapy
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule
from test_spider.items import TestSpiderItem
# class XcxsqSpider(CrawlSpider):
# name = 'xcxsq'
# allowed_domains = ['www.wxapp-union.com']
# start_urls = ['http://www.wxapp-union.com/portal.php?mod=list&catid=2&page=1']
# domains = 'http://www.wxapp-union.com/'
#
# rules = (
# # 这里不用 callback,因为这个是获取教程的每页的文章的 url,我们想要的是具体的每个文章的内容
# # follow=True 表示碰到同样规则的 url 需要跟进,scrapy会在返回的response中验证是否还有符合
# # 规则的条目,继续跳转发起请求抓取,周而复始
# Rule(LinkExtractor(
# allow=r'.+mod=list&catid=2&page=[1-10]'
# # restrict_xpaths='//'
# ), callback='parse_url', follow=True),
# # 这里需要 callback,因为这个是获取每个文章的具体内容,需要通过回调获取文章的内容
# # follow=False 表示碰到同样规则的 url 不跟进,因为文章内有推荐的,url 类似的文章,
# # 可能会和其他页中的文章有重复,导致爬取的文章顺序更变
# # Rule(LinkExtractor(
# # allow=r'.+article-.+\.html'
# # ),callback='parse_detail',follow=False)
# )
#
# def parse_url(self,response):
# urls = response.xpath('//ul[@id="itemContainer"]/div[@class="mbox_list recommend_article_list cl"]/a/@href').getall()
# for url in urls:
# page_url = self.domains + url
# print(page_url)
# yield scrapy.Request(page_url, callback=self.parse_detail, dont_filter=True)
#
# def parse_detail(self, response):
# # item = {}
# #item['domain_id'] = response.xpath('//input[@id="sid"]/@value').get()
# #item['name'] = response.xpath('//div[@id="name"]').get()
# #item['description'] = response.xpath('//div[@id="description"]').get()
# # return item
# title = response.xpath('//div[@class="h hm cl"]/div[@class="cl"]/h1[@class="ph"]/text()').get()
# author = response.xpath('//div[@class="avatar_right cl"]/div[@class="cl"]//a/text()').get()
# blockquote = response.xpath('//div[@class="blockquote"]/p/text()').get()
# # content = response.xpath('//div[@class="content_middle cl"]//td[@id="article_content"]/')
# print(title)
# print(author)
# print(blockquote)
class XcxsqSpider(CrawlSpider):
name = 'xcxsq'
allowed_domains = ['www.wxapp-union.com']
start_urls = ['http://www.wxapp-union.com/portal.php?mod=list&catid=2&page=1']
domains = 'http://www.wxapp-union.com/'
rules = (
# 这里不用 callback,因为这个是获取教程的每页的文章的 url,我们想要的是具体的每个文章的内容
# follow=True 表示碰到同样规则的 url 需要跟进,scrapy会在返回的response中验证是否还有符合
# 规则的条目,继续跳转发起请求抓取,周而复始
Rule(LinkExtractor(
allow=r'.+mod=list&catid=2&page=[1-10]'
), follow=True),
# 这里需要 callback,因为这个是获取每个文章的具体内容,需要通过回调获取文章的内容
# follow=False 表示碰到同样规则的 url 不跟进,因为文章内有推荐的,url 类似的文章,
# 可能会和其他页中的文章有重复,导致爬取的文章顺序更变
# 因为当前页有推荐文章,和想要爬取的文章会有重复,所以这里用 restrict_xpaths 限制选区而不是 allow
Rule(LinkExtractor(
restrict_xpaths='//ul[@id="itemContainer"]/div[@class="mbox_list recommend_article_list cl"]/a'
),callback='parse_detail',follow=False)
)
# def parse_url(self,response):
# urls = response.xpath('//ul[@id="itemContainer"]/div[@class="mbox_list recommend_article_list cl"]/a/@href').getall()
# for url in urls:
# page_url = self.domains + url
# print(page_url)
# yield scrapy.Request(page_url, callback=self.parse_detail, dont_filter=True)
def parse_detail(self, response):
# item = {}
#item['domain_id'] = response.xpath('//input[@id="sid"]/@value').get()
#item['name'] = response.xpath('//div[@id="name"]').get()
#item['description'] = response.xpath('//div[@id="description"]').get()
# return item
title = response.xpath('//div[@class="h hm cl"]/div[@class="cl"]/h1[@class="ph"]/text()').get()
author = response.xpath('//div[@class="avatar_right cl"]/div[@class="cl"]//a/text()').get()
blockquote = response.xpath('//div[@class="blockquote"]/p/text()').get()
artical_content = response.xpath('//td[@id="article_content"]//text()').getall()
content = ''.join(artical_content).strip()
item = TestSpiderItem(title=title, author=author, blockquote=blockquote, content=content)
yield item
pipelines.py
# 用 scrapy.exporters.JsonItemExporter
# JsonItemExporter 是将 item 都放入一个列表中,暂存在内存中,在 finish 时一起写入,比较耗内存
# JsonLinesItemExporter 是将 item 的字典逐行写入,节约内存,并且不用 start 和 finish
import json
from scrapy.exporters import JsonItemExporter, JsonLinesItemExporter
class TestSpiderPipeline(object):
# def __init__(self):
# # 以 bytes 格式写入
# self.fp = open('duanzi.json','wb')
# # self.exporter = JsonItemExporter(self.fp,ensure_ascii=False,encoding='utf8')
# self.exporter = JsonLinesItemExporter(self.fp,ensure_ascii=False,encoding='utf8')
# # self.exporter.start_exporting()
#
# def open_spider(self,spider):
# # with open('duanzi.json','w',encoding='utf8')
# print('spider is running!')
#
# def process_item(self, item, spider):
# self.exporter.export_item(item)
# # self.fp.write('\n')
# return item
#
# def close_spider(self,spider):
# # self.exporter.finish_exporting()
# self.fp.close()
# print('spider was closed!')
def __init__(self):
self.fp = open('xcx.json','wb')
self.exporter = JsonLinesItemExporter(self.fp, ensure_ascii=False, encoding='utf8')
def open_spider(self, spider):
print('spider is runing')
def process_item(self, item, spider):
self.exporter.export_item(item)
return item
def close_spider(self, spider):
print('spider was closed')
self.fp.close()
items.py
import scrapy
class TestSpiderItem(scrapy.Item):
# define the fields for your item here like:
# name = scrapy.Field()
# 将 json 中的 key 在这里定义,然后在解析数据中导入
title = scrapy.Field()
author = scrapy.Field()
blockquote = scrapy.Field()
content = scrapy.Field()