18--Scrapy04:CrawlSpider、链接提取器、源码模板文件
Scrapy04--CrawlSpider、链接提取器、源码模板文件
案例:汽车之家,全站抓取二手车的信息 来区分Spider 和 CrawlSpider
注意:汽车之家的访问频率 要控制一下, 要不然会跳验证
settings.py 中设置 DOWNLOAD_DELAY = 3
一、常规Spider
# spiders/Ershou.py
import scrapy
from scrapy.linkextractors import LinkExtractor
class ErshouSpider(scrapy.Spider):
name = 'ershou'
allowed_domains = ['che168.com']
start_urls = ['https://www.che168.com/china/a0_0msdgscncgpi1ltocsp1exx0/']
def parse(self, resp, **kwargs):
# print(resp.text)
# 链接提取器:详情页
le = LinkExtractor(restrict_xpaths=("//ul[@class='viewlist_ul']/li/a",), deny_domains=("topicm.che168.com",) )
links = le.extract_links(resp)
for link in links:
yield scrapy.Request(
url=link.url,
callback=self.parse_detail
)
# 链接提取器:翻页
le2 = LinkExtractor(restrict_xpaths=("//div[@id='listpagination']/a",))
pages = le2.extract_links(resp)
for page in pages:
yield scrapy.Request(url=page.url, callback=self.parse)
def parse_detail(self, resp, **kwargs):
title = resp.xpath('/html/body/div[5]/div[2]/h3/text()').extract_first()
print(title)
1.1 链接提取器
LinkExtractor
:链接提取器 类,可以非常方便从一个响应页面中,提取到url链接,只需要提前定义好规则即可.
### 1 构建 链接提取器类的 对象
from scrapy.linkextractors import LinkExtractor
le = LinkExtractor(参数) # 参数值 都是元祖形式
allow # 接收一堆正则表达式, 提取出符合该正则的链接
deny # 接收一堆正则表达式, 剔除符合该正则的链接
allow_domains # 接收一堆域名, 符合该域名的链接被提取
deny_domains # 接收一堆域名, 剔除不符合该域名的链接
restrict_xpaths # 接收一堆xpath, 提取符合要求xpath的链接
restrict_css # 接收一堆css选择器, 提取符合要求的css选择器的链接
tags # 设置从哪个标签中提取链接 默认(a,area)
attrs # 设置从标签的哪个属性中提取链接 默认 (href, )
其他参数 # 点击源码 自己看
### 2 根据响应对象 提取链接
links = 对象.extract_links(响应对象) # 且 自动将链接拼接完整了,不需要 response.urljoin(url)
# .extract_links() 返回的是links
一堆 Link对象(url='',text='', nofollow=False) 的列表
### 3 eg:
le = LinkExtractor(restrict_xpaths=("//ul[@class='viewlist_ul']/li/a",), deny_domains=("topicm.che168.com",) )
links = le.extract_links(resp)
# 注意:
在提取到的url中, 是有重复的内容的 但scrapy会自动过滤掉重复的url请求
二、CrawlSpider
2.1 工作流程
# 工作流程:
前期(构建起始页的请求对象) 和普通的spider是一致的
在第一次请求回来之后,会自动的将返回的response
按照rules中,多个订制的规则(Rule) 来匹配
进行提取链接,并进一步按照 规则的 callback参数 回调 进行具体的处理
# 形式
rules = (
Rule(LinkExtractor(restrict_xpaths=('xxx',)), follow=True), # 分页规则
Rule(LinkExtractor(restrict_xpaths=('yyy',)), callback='parse_item', follow=False), # 详情页规则
)
# 规则(Rule)的参数
1.第一个参数 link_extractor
参数值是LinkExtractor对象 # 链接提取器对象
2.callback='解析函数名' # 注意:和普通spider的区别是 该参数值是字符串形式 'parse_item' ,不再是函数地址 self.parse
3.follow=True | False
指的是按照 当前规则(Rule) 提取后的链接,发送链接请求后的 响应内容
是否还使用 当前所有规则(rules) 提取链接,并继续发送 链接的请求对象
相当于 常规spider中 在parse中继续 scrapy.request(xxx, callback=self.parse)
2.2 抓取汽车之家
在scrapy中提供了CrawlSpider,可用来完成全站数据抓取
注意:和以往的spider不同:该爬虫需要用到crawl的模板,来创建爬虫(spider)
- 创建项目
# 1.创建项目
scrapy startproject qichezhijia
# 2.进入项目
cd qichezhijia
# 3.创建爬虫 使用CrawlSpider -t 指定spider的模板名 eg:crawl ---> CrawlSpider
# scrapy genspider -t spider模板名 spider名 域名
scrapy genspider -t crawl ershouche che168.com
- 修改spider中的rules、回调函数
# spiders/Ershouche.py
import scrapy
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule # 使用crawl模板 会自动导入到这些
class ErshoucheSpider(CrawlSpider):
name = 'ershouche'
allowed_domains = ['che168.com', 'autohome.com.cn']
start_urls = ['https://www.che168.com/beijing/a0_0msdgscncgpi1ltocsp1exx0/']
# 分页规则
le1 = LinkExtractor(restrict_xpaths=("//div[@id='listpagination']/a",))
# 详情页规则
le2 = LinkExtractor(restrict_xpaths=("//ul[@class='viewlist_ul']/li/a",), deny_domains=("topicm.che168.com",) )
rules = (
Rule(le1, follow=True), # 分页规则 并响应内容 继续按照rules的所有规则 提取链接
Rule(le2, callback='parse_item', follow=False), # 详情页规则 并响应内容 不再继续按照rules的所有规则 提取链接了
)
def parse_item(self, response):
print(response.url)
# 注意:
该位置没有了parse函数了,上面一、常规spider中的parse函数的逻辑,都是集成到 CrawlSpider类中了。
三、源码模板文件
### scrapy源码的 模板文件
scrapy
-templates
--project # 创建项目的模板
---module
----spiders
__init__.py
items.py.tmpl
middlewares.py.tmpl
pipelines.py.tmpl
settings.py.tmpl # 默认配置的模板
---scrapy.cfg
---runner.py.tmpl # 自定义添加运行脚本runner.py.tmpl文件
--spiders # 创建爬虫(spider)的模板
basic.tmpl # 常规spider
crawl.tmpl # CrawlSpider
csvfeed.tmpl
xmlfeed.tmpl
### 应用1:修改源码 scrapy/templates/project/module/settings.py.tmpl
ROBOTSTXT_OBEY = False # 不遵守robots协议
LOG_LEVEL = 'WARNING' # 控制台打印日志级别
DOWNLOAD_DELAY = 3 # 请求的延迟 3秒
### 应用2:将运行脚本,添加到项目模板中
# 1.新建runner.py.tmpl 模板
scrapy/templates/project/runner.py.tmpl
from scrapy.cmdline import execute
if __name__ == '__main__':
execute('scrapy crawl to_modified_spider_name'.split())
# 注意:
运行runner.py,需要修改 to_modified_spider_name 为真正的spider名字
# 2.修改 scrapy创建项目时的命令文件 startproject.py
scrapy/commands/startproject.py
# 要渲染的模板
TEMPLATES_TO_RENDER = (
('scrapy.cfg',),
('${project_name}', 'settings.py.tmpl'),
('${project_name}', 'items.py.tmpl'),
('${project_name}', 'pipelines.py.tmpl'),
('${project_name}', 'middlewares.py.tmpl'),
('runner.py.tmpl',), # 添加运行脚本runner.py.tmpl文件
)