scrapy框架基础
一、Scrapy Shell
1.简介
Scrapy Shell是用来调试Scrapy项目代码的命令行工具,启动的时候预定义了Scrapy的一些对象
Scrapy Shell的作用:调试
2.设置
Scrapy 的shell是基于运行环境中的python 解释器shell,本质上就是通过命令调用shell,并在启动的时候预定义需要使用的对象
scrapy允许通过在项目配置文件”scrapy.cfg”中进行配置来指定解释器shell
# scrapy.cfg
[settings]
shell = ipython
注意:如果安装了ipython且没有指定解释器shell,会默认使用ipython
3.启动
启动Scrapy shell的命令
scrapy shell [url|file]
url 就是你想要爬取的网址,file是本地文件
注意:分析本地文件是一定要带上路径,scrapy shell会默认当作url
4.使用
[s] Available Scrapy objects:
[s] scrapy scrapy module (contains scrapy.Request, scrapy.Selector, etc)
[s] crawler <scrapy.crawler.Crawler object at 0x00000203907D6BA8>
[s] item {}
[s] settings <scrapy.settings.Settings object at 0x00000203907D6978>
[s] Useful shortcuts:
[s] fetch(url[, redirect=True]) Fetch URL and update local objects (by default, redirects are followed)
[s] fetch(req) Fetch a scrapy.Request and update local objects
[s] shelp() Shell help (print this help)
[s] view(response) View response in a browser
二、Selector
1.简介
scrapy提供了自己的数据提取方法,即selector(选择器)。selector是基于lxml来构建的,支持XPath选择器、CSS选择器以及正则表达式
response有一个属性selector,调用response.selector返回的内容相当于用text构造了一个Selector对象,进而调用以下方法,返回的结果是Selector组成的列表,是SelectorList类型,SelectorList类型可以继续调用以下方法,另外传入的response可以直接调用xpath()和css()方法
2.extract()
-
.extract():序列化该节点为Unicode字符串并返回list列表
-
.extract_first():序列化该节点为Unicode字符串并返回list列表的第一个值,列表为空时返回为空,不会报错,另外可设置默认值参数,列表为空时返回该参数,例如
.extract_first("no value")
,当列表为空时返回'no value'
3.xpath()
- .xpath(query):传入XPath表达式query,返回该表达式所对应的所有节点的SelectorList列表
4.css()
- .css(query):传入css表达式query,返回该表达式所对应的所有节点的SelectorList列表
5.正则表达式
-
.re(regex):根据传入的正则表达式对数据进行提取,返回Unicode字符串列表
-
.re_first(regex):根据传入的正则表达式对数据进行提取,返回Unicode字符串列表的第一个值
注意:response对象不能直接调用re()、re_first()方法,可以先调用xpath()方法再正则匹配:response.xpath('.').re(regex)
三、Spider
1.简介
实现Scrapy爬虫项目最核心的类是Spider类,它定义爬取网站的动作、分析爬取下来的网页
2.Spider运行流程
以初始URL初始化Request,并设置回调函数,当该Request请求成功并返回Response作为参数传给该回调函数,在回调函数中解析Response,一是得到有效结果返回字典或Item对象,二是解析出下一个网页链接,利用其构造Request并设置新的回调函数
3.Spider类分析
scrapy.Spider类是其他spider类的基类,所有spider类都必须继承scrapy.Spider类
scrapy.Spider类提供了start_requests()方法的默认实现,读取并请求start_urls属性,并根据返回的Response调用parse()方法
4.Spider类的常用属性
- name:爬虫名称,定义Spider名字的字符串,Spider最重要的属性。Scrapy使用name来定位和初始化Spider,因此它必须是唯一的,name一般使用爬取网站的域名
- allowed_domains:允许爬取的域名列表。可选配置,不在此范围的链接不会被跟进爬取
- start_urls:起始的URL列表。没有重写start_requests()方法时,默认从此列表开始爬取
- custom_settings:专属于本Spider的配置的字典。此配置在运行此Spider时将覆盖项目全局的设置。因为设置必须在初始化之前更新,所以必须将其定义为类属性
- logger:使用Spider创建的Python日志器,方法变属性。可以使用它来发送日志消息
5.Spider类的常用方法
- from_crawler():Scrapy用于创建spider的类方法,一般不用覆盖
- start_requests():用于生成初始请求,只会被调用一次,必须返回一个iterable(可迭代对象)。默认使用start_urls属性里的URL来构造GET请求格式的Request。深层次爬取时,启动阶段需要POST请求格式登录网站,获取用户权限,所以重写此方法,在发送POST请求时使用scrapy.FormRequest()方法
- parse():当Response没有指定回调函数时,该方法是Scrapy处理Response的默认方法。它负责处理Response,返回新的Request和Item的可迭代对象。必须重写否则会报错
- closed():spider关闭时被调用的方法。用于释放占用的资源
四、CrawlSpider
1.简介
CrawlSpider是Scrapy提供的通用Spider,可以指定URL爬取规则,自动生成Request实现页面的爬取,爬取规则由专门的数据结构Rule表示,Rule里包含提取和跟进页面的配置
2.创建方法
scrapy genspider -t crawl <name> <domain>
例如:scrapy genspider -t crawl js www.jianshu.com
在spiders文件夹下创建jianshu.py文件
# -*- coding: utf-8 -*-
import scrapy
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule
class JsSpider(CrawlSpider):
name = 'js'
allowed_domains = ['www.jianshu.com']
start_urls = ['http://www.jianshu.com/']
rules = (
Rule(LinkExtractor(allow=r'Items/'), callback='parse_item', follow=True),
)
def parse_item(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
2.属性与方法
CrawlSpider类继承自Spider类,除了Spider类的所有方法和属性,还提供下面的方法和属性
- rules:定义爬取规则的属性,是包含一个或多个Rule对象的列表。即使只有一个Rule实例,也要用逗号分隔。每个Rule对爬取网站的动作都有定义,CrawlSpider会读取rules的每个Rule并进行解析
- parse_start_url(response):当start_url的请求返回时被调用,功能是分析Response并返回Item对象或Request对象,是可重写的方法
4.Rule类的参数
class Rule(object):
def __init__(self, link_extractor=None, callback=None, cb_kwargs=None, follow=None, process_links=None, process_request=None)
- link_extractor:是LinkExtractor对象。其定义了如何从爬取到的页面提取链接生成Request
- callback:回调函数。每次从link_extractor中获取链接时被调用,其接收一个Response作为第一个参数,并返回Item或Request对象。应尽量避免使用parse()做回调函数,因为CrawlSpider使用parse()实现其逻辑,如果覆盖了parse(),CrawlSpider将会运行失败
- cb_kwargs:包含传递给回调函数的参数的字典
- follow:布尔值,指定是否根据此规则提取的每个响应中跟踪链接
- process_links:用于过滤链接的回调函数
- process_request:用于过滤请求的回调函数
5.LinkExtractor类的参数
class LxmlLinkExtractor(FilteringLinkExtractor):
def __init__(self, allow=(), deny=(), allow_domains=(), deny_domains=(), restrict_xpaths=(),
tags=('a', 'area'), attrs=('href',), canonicalize=False,
unique=True, process_value=None, deny_extensions=None, restrict_css=(),
strip=True, restrict_text=None):
- allow:正则表达式,满足的URL被提取出来
- deny:正则表达式,满足的URL被排除,优先级高于allow
- allow_domains:允许的域名,str或list
- deny_domains:排除的域名,str或list
- restrict_xpaths:xpath表达式,满足的URL被提取出来,str或list
- restrict_css:css选择器,满足的URL被提取出来,str或list
- tags:提取指定标记下的链接,默认为a,area
- attrs:提取拥有满足属性的链接,默认为href
- unique:是否过滤相同的URL
- process_value:值处理函数,优先级大于allow
6.CrawlSpider URL去重
CrawlSpider的URL去重是基于CrawlSpider类里面的私有方法_requests_to_follow()
def _requests_to_follow(self, response):
if not isinstance(response, HtmlResponse): # 如果接受的response不是HtmlResponse对象就跳出
return
seen = set()
for n, rule in enumerate(self._rules):
links = [lnk for lnk in rule.link_extractor.extract_links(response)
if lnk not in seen] # 对生成的Request查重
if links and rule.process_links:
links = rule.process_links(links) # 利用process_links()过滤
for link in links:
seen.add(link)
request = self._build_request(n, link)
yield rule._process_request(request, response)
7.实例:爬取简书文章数据
通过CrawlSpider类,爬取简书网站文章数据:作者、标题、发布时间、阅读数
items.py
import scrapy
class JianshuItem(scrapy.Item):
author = scrapy.Field()
title = scrapy.Field()
release_time = scrapy.Field()
pageview = scrapy.Field()
js.py
# -*- coding: utf-8 -*-
import scrapy
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule
from jianshu.items import JianshuItem
class JsSpider(CrawlSpider):
name = 'js'
# allowed_domains = ['www.jianshu.com']
start_urls = ['https://www.jianshu.com/']
rules = (
Rule(LinkExtractor(allow=r'https://www.jianshu.com/p/\w+'), callback='parse_item', follow=True),
)
def parse_item(self, response):
item = JianshuItem()
item['author'] = response.xpath('//span[@class="_22gUMi"]/text()').extract_first()
item['title'] = response.xpath('//h1[@class="_1RuRku"]/text()').extract_first()
item['release_time'] = response.xpath('.').re('"first_shared_at":(\d+),')[0]
item['pageview'] = response.xpath('.').re('"views_count":(\d+)')[-1]
yield item
settings.py
ROBOTSTXT_OBEY = False
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(Macintosh;IntelMacOSX10_7_0)AppleWebKit/535.11(KHTML,likeGecko)Chrome/17.0.963.56Safari/535.11'
}
五、请求与响应
1.Request
Scrapy.http.Request类是scrapy框架中Request的基类
class Request(object_ref):
def __init__(self, url, callback=None, method='GET', headers=None, body=None,
cookies=None, meta=None, encoding='utf-8', priority=0,
dont_filter=False, errback=None, flags=None, cb_kwargs=None)
-
Request对象的参数
- url(string):请求的URL
- callback(callable):回调函数
- method(string):HTTP请求方式,默认为’GET’
- meta(dict):Request.meta属性的初始值
- body(str or unicode):请求体。如果没有传参,默认为空字符串
- headers(dict):请求的请求头
- cookies(dirt or list):请求的cookie信息
- encoding(string):请求的编码,默认为’utf-8’
- priority(int):请求的优先级(默认为0),被调度器用来安排处理请求的顺序,数字越大优先级越高(可以为负)
- dont_filter(boolean):表示调度程序不应过滤此请求(用于多次执行相同请求,小心掉入爬虫循环)
- errback(callable):在处理请求时引发任何异常时被调用的函数
- flags(list):发送给请求的标志,可用于日志记录或类似目的
-
Request对象属性和方法
- url:请求的URL的字符串。该属性是只读的,更改请求使用的URL replace()
- method:HTTP请求方式
- headers: 类似字典的对象,包含请求头信息
- body:请求正文的string。该属性是只读的,更改请求使用的URL replace()
- meta:跟随请求并包含元数据的字典,可往其中添加字段
- copy():返回一个新的请求,该请求是复制此请求的副本
- replace([ URL,method,headers,body,cookies,meta,encoding,dont_filter,callback,errback] ) :可替换Request中相对应的内容,返回一个新的request
2.FormRequest(登录操作)
FormRequest类的基类是Request,它专门用来处理HTML表单,适合用来完成登录操作
在处理隐藏表单时,FormRequest类提供了一个类方法FormResponse
class FormRequest(Request):
...
@classmethod
def from_response(cls, response, formname=None, formid=None, formnumber=0, formdata=None,clickdata=None, dont_click=False, formxpath=None, formcss=None, **kwargs)
...
- FormResponse的参数
- response(Responseobject):包含HTML表单的响应
- formname(string):如果给定,将使用name属性设置为此值的表单,否则为None
- formid(string):如果给定,将使用id属性设置为此值的表单,否则为None
- formxpath(string):如果给定,将使用与xpath匹配的第一个表单,否则为None
- formnumber(整数):当响应包含多个表单时将使用第几个表单
- formdata(dict):要在表单数据中覆盖的字段
- formcss(string):如果给定,将使用与css选择器匹配的第一个表单,否则为None
- clickdata(dict):用于查找单击控件的属性
- dont_click(boolean):如果为True,将提交表单数据而不单击任何元素
3.实例:豆瓣登录
import scrapy
class DbSpider(scrapy.Spider):
name = 'db'
start_urls = ['https://accounts.douban.com/passport/login']
login_url = 'https://accounts.douban.com/passport/login'
def parse(self, response):
formdata = {
"name": "18340760809",
"password": "zzh123456",
"remember": "false",
}
yield scrapy.FormRequest(self.login_url, formdata=formdata, callback=self.login_after)
def login_after(self, response):
res = response.xpath('//a[@class="bn-more"]/span/text()').extract_first()
if res is None:
print("登录失败")
else:
print("登陆成功,当前账户为:%s" % res)
4.Response
scrapy.http.Response类是scrapy框架中Response的基类
class Response(object_ref):
def __init__(self, url, status=200, headers=None, body=b'', flags=None, request=None)
属性和方法:
url:只读属性,响应的URL
status:响应码
headers:响应头
body:响应体
request:产生此响应的请求
meta:Request.meta属性的初始值
flags:附加在Response上的标记
copy():复制出一个新的Response实例
replace([ url,status,headers,body,request,flags,cls ] ):替换掉Response中相对应的内容,返回一个新的Response实例
urljoin(url):URL拼接,用来将Response.url和一个相对的URL,构成一个绝对的URL
follow(url):自动进行URL拼接,其中调用了urljoin(url)方法
5 .实例:爬取腾讯招聘招聘信息
模拟爬取腾讯招聘招聘信息:工作、地点、类型、详情(现该网站已经有反爬,因此以下代码已经失效,重点在于体验meta传播过程)
ct.py
# -*- coding: utf-8 -*-
import scrapy
from ..items import CareersTencentItem
class CtSpider(scrapy.Spider):
name = 'ct'
# allowed_domains = ['https://careers.tencent.com/search.html']
start_urls = ['https://careers.tencent.com/search.html']
def parse(self, response):
trs = response.xpath('//div[@class="recruit-list"]/a[@class="recruit-list-link"]')
for i in trs:
item = CareersTencentItem()
item['job_name'] = response.xpath('.//h4[@class="recruit-title"]/text()]').extract_first()
item['job_addr'] = response.xpath('.//p[@class="recruit-tips"]/span[2]/text()').extract_first()
item['job_type'] = response.xpath('.//p[@class="recruit-tips"]/span[3]/text()').extract_first()
url_detail = response.xpath('.//div[@class="share-list"]/div[3]/@id').extract_first()
yield scrapy.Request(url='https://careers.tencent.com/search.html?postId=%d' % url_detail,callback=self.parse_detail, meta={'job_item': item})
# 请求下一页
next_url = response.xpath('//ul[@class="page-list"]/li[@class="page-li active"]/span/text()').extract_first()
if next_url:
yield scrapy.Request(url='https://careers.tencent.com/search.html?index=%d' % next_url, callback=self.parse)
def parse_detail(self, response):
item = response.meta['job_item']
item['detail'] = response.xpath('//li[@class="explain-item"]/text()').extract()
yield item
items.py
import scrapy
class CareersTencentItem(scrapy.Item):
job_name = scrapy.Field()
job_addr = scrapy.Field()
job_type = scrapy.Field()
job_detail = scrapy.Field()
6.日志的配置和使用
(1).在spider中记录
def parse(self, response):
self.logger.info("这是一个info")
(2).在其他组件中记录
import logging
logger = logging.getLogger("my_logger")
logging.warning("这是一个warning")
(3).settings.py中配置日志记录
这些设置可用于配置日志记录:
- LOG_FILE:日志输出文件,如果为None就打印在控制台
- LOG_ENABLED:是否启用日志,默认True
- LOG_ENCODING:日期编码,默认utf-8
- LOG_LEVEL:日志等级,默认debug
- LOG_FORMAT:日志格式
- LOG_DATEFORMAT:日志日期格式
- LOG_STDOUT:日志标准输出,默认False,如果True所有标准输出(print)都将写入日志中
- LOG_SHORT_NAMES:短日志名,默认为False,如果True将不输出组件名
LOG_FILE:'log.log'
LOG_ENABLED:True
LOG_ENCODING:'utf-8'
LOG_LEVEL:'INFO'
LOG_FORMAT:"%(asctime)s-%(levelname)s-%(message)s"
LOG_DATEFORMAT:"%Y-%m-%d %H:%M:%S"
LOG_STDOUT:False
LOG_SHORT_NAMES:False
六、Downloader Middlewares
1.简介
下载中间件是实现了特殊方法的类,用于全局修改Scrapy的Request和Response,定制自己的爬虫(修改User-Agent、处理重定向、设置代理、设置cookie等)
2.内置下载中间件
官方文档:https://docs.scrapy.org/en/latest/topics/downloader-middleware.html
DOWNLOADER_MIDDLEWARES_BASE变量定义了Scrapy的内置下载中间件,它是字典格式,键是Scrapy内置的下载中间件的类路径,值是调用的优先级(正整数0-1000),值越小越靠近引擎
# 这是默认的值
DOWNLOADER_MIDDLEWARES_BASE = {
'scrapy.downloadermiddlewares.robotstxt.RobotsTxtMiddleware': 100,
'scrapy.downloadermiddlewares.httpauth.HttpAuthMiddleware': 300,
'scrapy.downloadermiddlewares.downloadtimeout.DownloadTimeoutMiddleware': 350,
'scrapy.downloadermiddlewares.defaultheaders.DefaultHeadersMiddleware': 400,
'scrapy.downloadermiddlewares.useragent.UserAgentMiddleware': 500,
'scrapy.downloadermiddlewares.retry.RetryMiddleware': 550,
'scrapy.downloadermiddlewares.ajaxcrawl.AjaxCrawlMiddleware': 560,
'scrapy.downloadermiddlewares.redirect.MetaRefreshMiddleware': 580,
'scrapy.downloadermiddlewares.httpcompression.HttpCompressionMiddleware': 590,
'scrapy.downloadermiddlewares.redirect.RedirectMiddleware': 600,
'scrapy.downloadermiddlewares.cookies.CookiesMiddleware': 700,
'scrapy.downloadermiddlewares.httpproxy.HttpProxyMiddleware': 750,
'scrapy.downloadermiddlewares.stats.DownloaderStats': 850,
'scrapy.downloadermiddlewares.httpcache.HttpCacheMiddleware': 900,
}
3.方法
不是所有的方法都需要定义,如果没有定义方法,则在Scrapy中相当于下载中间件没有修改传递的对象
from scrapy import signals
class DoubanDownloaderMiddleware(object):
@classmethod
def from_crawler(cls, crawler): # Scrapy用此方法创建你的Spiders对象
s = cls()
crawler.signals.connect(s.spider_opened, signal=signals.spider_opened)
return s
def process_request(self, request, spider):
return None
def process_response(self, request, response, spider):
return response
def process_exception(self, request, exception, spider):
pass
def spider_opened(self, spider):
spider.logger.info('Spider opened: %s' % spider.name)
(1).process_request(request, spider)
在Request到下载器之前对Request进行处理的方法
- 参数
- request:被处理的Request对象
- spider:此Request对应的Spider
- 返回类型
- None:正常执行其他下载中间件的此方法
- request:停止执行低优先级的其他下载中间件,将此request重新放入调度器队列,等待其被调度
- response:跳过更低级别的其他下载中间件,直接开始依次执行process_response()方法
- IgnoreRequest异常:直接开始依次执行process_exception()方法
(2).process_response(request, response, spider)
在Response到达Spiders之前对Response进行处理的方法
- 参数
- request:此Response对应的Request
- response:被处理的Response对象
- spider:此Response对应的Spider
- 返回类型
- request:停止执行低优先级的其他下载中间件,将此request重新放入调度器队列,等待其被调度
- response:正常执行其他下载中间件的此方法
- IgnoreRequest异常:直接开始依次执行process_exception()方法
(3).process_exception(request, exception, spider)
当Downloader或者process_request()方法抛出异常时调用此方法
- 参数
- request:被处理的Request对象
- spider:此Request对应的Spider
- 返回类型
- None:正常执行其他下载中间件的此方法
- request:停止执行低优先级的其他下载中间件,将此request重新放入调度器队列,等待其被调度
- response:跳过更低级别的其他下载中间件,直接开始依次执行process_response()方法
(4).from_crawler(cls, crawler)
类方法,参数是crawler,通过crawler对象可以拿到Scrapy的所有核心组件
4.激活下载中间件
在settings.py中对DOWNLOADER_MIDDLEWARES进行设置,会与Scrapy内置的下载中间件DOWNLOADER_MIDDLEWARES_BASE合并,但是不会覆盖,依旧按照优先级执行
如果想要覆盖某项,直接将DOWNLOADER_MIDDLEWARES中设置此项使其优先级在内置下载中间件之后即可
# 'scrapy.downloadermiddlewares.useragent.UserAgentMiddleware': 500,
DOWNLOADER_MIDDLEWARES = {
'douban.middlewares.RandomUserAgentMiddleware': 900,
}
或者直接将其对应的内置下载中间件中的此项在DOWNLOADER_MIDDLEWARES中设置为None即可
DOWNLOADER_MIDDLEWARES = {
'scrapy.downloadermiddlewares.useragent.UserAgentMiddleware': None,
'douban.middlewares.RandomUserAgentMiddleware': 300,
}
5.搭建UA池
middlewares.py
from scrapy import signals
import random
class RandomUserAgentMiddleware(object):
def __init__(self, user_agents):
self.user_agents = user_agents
@classmethod
def from_crawler(cls, crawler):
# 在settings.py文件中加载MY_USER_AGENTS的值
s = cls(user_agents=crawler.settings.get('MY_USER_AGENTS'))
return s
def process_request(self, request, spider):
# 随机设置User-Agent的值
agent = random.choice(self.user_agents)
# 将其赋给Request
request.headers['User-Agent'] = agent
# 如果设置代理IP通过meta传参
# proxy = random.choice(self.proxy)
# request.meta['proxy'] = proxy
return None
settings.py
# 激活UA池
DOWNLOADER_MIDDLEWARES = {
'scrapy.downloadermiddlewares.useragent.UserAgentMiddleware': None,
'douban.middlewares.RandomUserAgentMiddleware': 300,
}
# UA池
MY_USER_AGENTS = [
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 "
"(KHTML, like Gecko) Chrome/22.0.1207.1 Safari/537.1",
"Mozilla/5.0 (X11; CrOS i686 2268.111.0) AppleWebKit/536.11 "
"(KHTML, like Gecko) Chrome/20.0.1132.57 Safari/536.11",
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.6 "
"(KHTML, like Gecko) Chrome/20.0.1092.0 Safari/536.6",
"Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.6 "
"(KHTML, like Gecko) Chrome/20.0.1090.0 Safari/536.6",
"Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.1 "
"(KHTML, like Gecko) Chrome/19.77.34.5 Safari/537.1",
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/536.5 "
"(KHTML, like Gecko) Chrome/19.0.1084.9 Safari/536.5",
"Mozilla/5.0 (Windows NT 6.0) AppleWebKit/536.5 "
"(KHTML, like Gecko) Chrome/19.0.1084.36 Safari/536.5",
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 "
"(KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3",
"Mozilla/5.0 (Windows NT 5.1) AppleWebKit/536.3 "
"(KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_0) AppleWebKit/536.3 "
"(KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3",
"Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 "
"(KHTML, like Gecko) Chrome/19.0.1062.0 Safari/536.3",
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 "
"(KHTML, like Gecko) Chrome/19.0.1062.0 Safari/536.3",
"Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 "
"(KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3",
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 "
"(KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3",
"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/536.3 "
"(KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3",
"Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 "
"(KHTML, like Gecko) Chrome/19.0.1061.0 Safari/536.3",
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.24 "
"(KHTML, like Gecko) Chrome/19.0.1055.1 Safari/535.24",
"Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/535.24 "
"(KHTML, like Gecko) Chrome/19.0.1055.1 Safari/535.24"
]
利用scrapy shell https://httpbin.org
测试即可
七、Spider Middlewares(使用频率低)
1.简介
蜘蛛中间件是实现了特殊方法的类,用于下载器生成的Response传到蜘蛛之前处理Response,蜘蛛生成的Request传到调度器之前处理Request,蜘蛛生成的Item传到Item管道之前处理Item,但是一般都在蜘蛛中定义,所以其使用频率很低
2.内置蜘蛛中间件
官方文档: https://docs.scrapy.org/en/latest/topics/spider-middleware.html
SPIDER_MIDDLEWARES_BASE变量定义了Scrapy的内置蜘蛛中间件,它是字典格式,键是Scrapy内置的蜘蛛中间件的类路径,值是调用的优先级(正整数0-1000),值越小越靠近引擎
SPIDER_MIDDLEWARES_BASE = {
'scrapy.spidermiddlewares.httperror.HttpErrorMiddleware': 50,
'scrapy.spidermiddlewares.offsite.OffsiteMiddleware': 500,
'scrapy.spidermiddlewares.referer.RefererMiddleware': 700,
'scrapy.spidermiddlewares.urllength.UrlLengthMiddleware': 800,
'scrapy.spidermiddlewares.depth.DepthMiddleware': 900,
}
3.方法
不是所有的方法都需要定义,如果没有定义方法,则在Scrapy中相当于蜘蛛中间件没有修改传递的对象
from scrapy import signals
class DoubanSpiderMiddleware(object):
@classmethod
def from_crawler(cls, crawler):
s = cls()
crawler.signals.connect(s.spider_opened, signal=signals.spider_opened)
return s
def process_spider_input(self, response, spider):
return None
def process_spider_output(self, response, result, spider):
for i in result:
yield i
def process_spider_exception(self, response, exception, spider):
pass
def process_start_requests(self, start_requests, spider):
for r in start_requests:
yield r
def spider_opened(self, spider):
spider.logger.info('Spider opened: %s' % spider.name)
(1).process_spider_input(response, spider)
当Response被蜘蛛中间件处理时执行的方法
- 参数
- response:被处理的Response
- spider:该Response的Spider
- 返回类型
- None:正常继续处理该Response
- 异常:跳过其他蜘蛛中间件的此方法
(2).process_spider_output(response, result, spider)
当蜘蛛处理Response返回结果时执行的方法
- 参数
- response:生成该输出的Response
- result:包含Request或者Item对象的可迭代对象
- spider:该Response的Spider
- 返回类型
- 包含Request或者Item对象的可迭代对象(必须)
(3).process_spider_exception(response, exception, spider)
当蜘蛛或蜘蛛中间件的process_spider_input()方法抛出异常时执行的方法
- 参数
- response:处理时抛出异常的Response
- exception:被抛出的异常
- spider:抛出该异常的Spider
- 返回类型
- None:正常继续处理该异常,调用其他蜘蛛中间件的此方法
- 可迭代对象:调用其他蜘蛛中间件的process_spider_output()方法
(4).process_start_requests(start_requests, spider)
当蜘蛛处理新的Request时执行的方法
- 参数
- start_requests:包含Request的可迭代对象
- spider:start_requests所属的Spider
- 返回类型
- 包含Request对象的可迭代对象(必须)
4.激活蜘蛛中间件
在settings.py中对SPIDER_MIDDLEWARES进行设置
SPIDER_MIDDLEWARES = {
'douban.middlewares.DoubanSpiderMiddleware': 543,
}
八、Item Pipelin
1.简介
Item Pipeline的主要用途是:
- 清理HTML数据
- 验证爬取数据,检查爬取字段
- 查重并删除重复内容
- 将爬取结果保存到数据库
2.方法
(1).process_item(item, spider)
必须实现的方法,被定义的Item管道会默认调用此方法对Item进行处理
- 参数
- item:被处理的Item
- spider:生成该Item的Spider
- 返回类型
- Item对象:此Item被低优先级的Item管道的此方法继续处理
- DropItem异常:丢弃此Item,不再进行处理
(2).open_spider(spider)
Spider开启的时候被自动调用的方法,此处可以开启数据库连接或打开文件
(3).close_spider(spider)
Spider关闭的时候被自动调用的方法,此处可以关闭数据库连接或关闭文件
3.激活Item管道
在settings.py中对ITEM_PIPELINES进行设置
ITEM_PIPELINES = {
'douban.pipelines.DoubanPipeline': 300,
}
九、settings
1.优先级
- 命令行选项(优先级最高)
- 设置per-spider
- 项目设置模块
- 各命令默认设置
- 默认全局设置(低优先级)
2.settings.py
# Scrapy settings for one project
# 项目名称
BOT_NAME = 'one'
SPIDER_MODULES = ['one.spiders']
NEWSPIDER_MODULE = 'one.spiders'
# 用户UA
#USER_AGENT = 'one (+http://www.yourdomain.com)'
# 是否遵循robots协议
ROBOTSTXT_OBEY = True
# 配置由Scrapy执行的最大并发请求(默认值:16)
#CONCURRENT_REQUESTS = 32
# 为同一网站的请求配置延迟(默认:0)
#DOWNLOAD_DELAY = 3
# 最多同时并发处理每个域名、IP数
#CONCURRENT_REQUESTS_PER_DOMAIN = 16
#CONCURRENT_REQUESTS_PER_IP = 16
# 是否启用cookies
#COOKIES_ENABLED = False
# 是否启用远程登录控制台
#TELNETCONSOLE_ENABLED = False
# 覆盖默认的请求头
#DEFAULT_REQUEST_HEADERS = {
# 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
# 'Accept-Language': 'en',
#}
# 爬虫中间件配置
#SPIDER_MIDDLEWARES = {
# 'one.middlewares.OneSpiderMiddleware': 543,
#}
# 下载中间件配置
#DOWNLOADER_MIDDLEWARES = {
# 'one.middlewares.OneDownloaderMiddleware': 543,
#}
# 启用或禁用扩展
# See https://docs.scrapy.org/en/latest/topics/extensions.html
#EXTENSIONS = {
# 'scrapy.extensions.telnet.TelnetConsole': None,
#}
# 项目管道配置
#ITEM_PIPELINES = {
# 'one.pipelines.OnePipeline': 300,
#}
# 启用和配置自动节流阀扩展(默认禁用)
# See https://docs.scrapy.org/en/latest/topics/autothrottle.html
#AUTOTHROTTLE_ENABLED = True
# 初始下载延迟
#AUTOTHROTTLE_START_DELAY = 5
# 在高延迟情况下设置的最大下载延迟
#AUTOTHROTTLE_MAX_DELAY = 60
# 每个远程服务器并行发送的请求的平均数量
#AUTOTHROTTLE_TARGET_CONCURRENCY = 1.0
# 是否显示收到的每个响应的节流状态
#AUTOTHROTTLE_DEBUG = False
# 启用和配置HTTP缓存(默认禁用)
# See https://docs.scrapy.org/en/latest/topics/downloader-middleware.html#httpcache-middleware-settings
#HTTPCACHE_ENABLED = True
#HTTPCACHE_EXPIRATION_SECS = 0
#HTTPCACHE_DIR = 'httpcache'
#HTTPCACHE_IGNORE_HTTP_CODES = []
#HTTPCACHE_STORAGE = 'scrapy.extensions.httpcache.FilesystemCacheStorage'
十、项目准备工作
1.项目需求
- 设计数据库用于存储
- 抓取内容,并自动入库
- 要求分布式部署
- 定时增量爬取
2.设计数据库
Navicat是一套快速、可靠并价格相当便宜的数据库管理工具,以直觉化的图形用户界面而建的,可以用来对本机或远程的 MySQL、SQL Server、SQLite、Oracle 及 PostgreSQL 数据库进行管理及开发
3.爬取思路
- 分析网站结构,决定爬取流程
- 先爬取标题信息,并入库,获取id
- 再爬取详细信息,根据id进行入库
- 设置爬取频率
- 加上UA池、代理池、cookie池