爬虫
爬虫
什么是爬虫?就是伪造浏览器,下载网页源代码,再从源代码获取需要的数据,一般分为两步: 1、伪造浏览器下载网页(requests模块) 2、对网页的内容进行结构化处理(BeautifulSoup模块)requests模块
安装:pip install requests 下面是这个模块常用到的方法、属性# 实例化对象,把所有的内容都封装到对象里,以下是get请求 response = requests.get(url="https://XXXXXXX") # post请求,某些网站做了限制,需要headers中添加User-Agent、Host、Referer response = requests.post(url="https://XXXXXXX",data={},headers={}, cookies={}) # 获取cookies response.cookies.get_dict() # 获取状态码 print(response.status_code) # 获取文本内容, print(response.text) # 但此文本内容是乱码,因为默认用的是utf8,而此文本用的是gbk # 设置编码 response.encoding = 'gbk' print(response.text) # 获取二进制格式的文本内容 print(response.content)
关于请求头中Content-Type
这个头主要有两种类型application/x-www-form-urlencoded;charset=UTF-8和application/json;charset=UTF-8 第一种主要是用于发送简单数据,如简单字典{'k1':'v1','k2':'v2}, 第二种主要是用于发送复杂数据,如复杂字典,值中又包含字典的,案例如下:# django服务器端 def server(request): # request.POST只能处理简单请求,也就是请求头中Content-Type是application/x-www-form-urlencoded;charset=UTF-8 # print(request.POST) # 要处理复杂请求,也就是请求头中Content-Type是application/json;charset=UTF-8 # 需要从request.body中获取,再解码,然后再json.loads print(json.loads(request.body.decode('utf-8'))) return HttpResponse('已接收') # 客户端 import requests import json # 发送简单数据给服务器 # response = requests.post( # url=' http://127.0.0.1:8000/server/', # headers={ # # 默认值,可不设置 # 'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8', # }, # data={'name': 'Treelight', 'age': 18} # ) # print(response.text) # 发送复杂数据比如字典的值只能为列表、数字、字符串等,不可以用字典,如以下的data # 这就请求头中的Content-Type用到application/json格式,同时需要用到json # response = requests.post( # url=' http://127.0.0.1:8000/server/', # headers={ # # 默认值 # 'Content-Type': 'application/json;charset=UTF-8', # }, # data=json.dumps({ # 'disk': ['disk1', 'disk2'], # 'cpu': { # 1: 'cpu1', # 2: 'cpu2', # 3: 'cpu3', # } # }) # ) # print(response.text) # 发送复杂数据的第二种形式 response = requests.post( url=' http://127.0.0.1:8000/server/', json=({ 'disk': ['disk1', 'disk2'], 'cpu': { 1: 'cpu1', 2: 'cpu2', 3: 'cpu3', } }) ) print(response.text)
requests参数
response = requests.request( method='get', # 请求方式 url="", # 请求地址 params = {}, # get形式传的参数 requests.get(url='http://www.cnblogs.com/wupeiqi',params={'id':1,'page':2}) data={}, # 发送的请求体内容, json={}, # 发送的请求体内容,内部json.dumps({....}) headers={}, cookies={}, proxies={}, # 代理、 files = (), #上传文件 auth=None, # 简单常用的验证规则 timeout=(1,2), # 连接超时;响应超时 allow_redirects=True, # 允许重定向 stream=True, # 流式下载 cert=(), # 证书 )
import requests session = requests.Session() ### 1、首先登陆任何页面,获取cookie i1 = session.get(url="http://dig.chouti.com/help/service") ### 2、用户登陆,携带上一次的cookie,后台对cookie中的 gpsd 进行授权 i2 = session.post( url="http://dig.chouti.com/login", data={ 'phone': "8615131255089", 'password': "xxxxxx", 'oneMonth': "" } ) i3 = session.post( url="http://dig.chouti.com/link/vote?linksId=8589623", ) print(i3.text)
更多请参考https://www.cnblogs.com/wupeiqi/articles/5354900.html
请求体:FormData
Request
用户凭证:响应体 cookie中
BeautifulSoup模块
安装:pip install BeautifulSoup4 这个模块是对下载的html或xml内容进行结构化处理的 常用的属性、方法如下:# 把文本传过去,然后用python自带的html解释器处理,进行结构化,返回的是顶级结构 soup = BeautifulSoup(response.text, 'html.parser') # 找到id为i1的标签 tag1 = soup.select('#i1') # 属性的增删改 del tag1.attrs['name'] tag1.attrs['name'] = 'username' # 如果没有此属性则会增加 tag1.attrs['result'] = 'Good' # 找孩子 tag1.children # 找子子孙孙 tag1.descendsants # 删除标签里的所有内容,但不包括自己 tag1.clear # 全部删除,包括自己 tag1.decompose() # 全部删除,包括自己,而且有返回值 ret = tag1.extra() # 转换成字符串 tag1.decode() # 转换成字符串,但不包括自己 tag1.decode_contents() # 转换成字节 tag1.encode() # 转换成字节,但不包括自己 tag1.encode_contents() # 找第一个标签,文本为Beckham tag2 = soup.find(name='div', attrs={'class':'form-control','id':'username'}, recursive=True, text='Beckham') # 和上面效果一样的,但注意class需要有下划线,建议使用上面的 tag2 = soup.find(name='div', class_='form-control',id='username', recursive=True, text='Beckham') # find_all:找到第一个div标签 tag3 = soup.find_all(name='div', limit=1) # find_all要找的属性值可以是列表,以下是找到div或a标签 tag3 = soup.find_all(name=['div', 'a']) # 找到类为form-control或myform的标签 tag3 = soup.find_all(class=['form-control', 'myform']) # 也可为正则 reg = re.compile('form-.*') tag3 = soup.find_all(class=reg) # 也可为函数,找到同时有class和id属性的标签 def func(tag): return tag.has_attr('class') and tag.has_attr('id') v = soup.find_all(name=func) print(v) # 标签是否有此属性 tag3.has_attr('class') # 获取文本 tag3.get_text()
更多请参考https://www.cnblogs.com/wupeiqi/articles/6283017.html
例一、爬取汽车之家的数据
import requests from bs4 import BeautifulSoup # 伪造浏览器,下载页面 # 实例化对象,把所有的内容都封装到对象里 response = requests.get(url="https://www.autohome.com.cn/news/") # 获取状态码 # print(response.status_code) # 获取文本内容, # print(response.text) # 但此文本内容是乱码,因为默认用的是utf8,而此文本用的是gbk # 设置编码 response.encoding = 'gbk' # print(response.text) # 获取二进制格式的文本内容 # print(response.content) # 下载页面完毕 # 结构化处理开始 # 把文本传过去,然后用python自带的html解释器处理,进行结构化,返回的是顶级结构 soup = BeautifulSoup(response.text, 'html.parser') # 找到id是auto-channel-lazyload-article的div 标签,返回的是Tag对象 div = soup.find(name='div', id='auto-channel-lazyload-article') # 从tag对象可继续找其下面的孩子,如找其li,find_all返回的是Tag对象列表 li_list = div.find_all(name='li') for li in li_list: h3 = li.find(name='h3') a = li.find(name='a') p = li.find(name='p') img = li.find(name='img') if not h3: continue # 获取tag对象文本 print(h3.text) # 获取属性 print(a.get('href')) print(p.text) # 下载图片 img_url = 'https:' + img.get('src') filename = img_url.rsplit('/', maxsplit=1)[1] img_res = requests.get(img_url) with open(filename, 'wb') as f: # 需要的是二进制格式 f.write(img_res.content) # 下载图片结构 print('-------------------------------------------------')**------------恢复内容开始------------**
爬虫
什么是爬虫?就是伪造浏览器,下载网页源代码,再从源代码获取需要的数据,一般分为两步: 1、伪造浏览器下载网页(requests模块) 2、对网页的内容进行结构化处理(BeautifulSoup模块)requests模块
安装:pip install requests 下面是这个模块常用到的方法、属性# 实例化对象,把所有的内容都封装到对象里,以下是get请求 response = requests.get(url="https://XXXXXXX") # post请求,某些网站做了限制,需要headers中添加User-Agent、Host、Referer response = requests.post(url="https://XXXXXXX",data={},headers={}, cookies={}) # 获取cookies response.cookies.get_dict() # 获取状态码 print(response.status_code) # 获取文本内容, print(response.text) # 但此文本内容是乱码,因为默认用的是utf8,而此文本用的是gbk # 设置编码 response.encoding = 'gbk' print(response.text) # 获取二进制格式的文本内容 print(response.content)
关于请求头中Content-Type
这个头主要有两种类型application/x-www-form-urlencoded;charset=UTF-8和application/json;charset=UTF-8 第一种主要是用于发送简单数据,如简单字典{'k1':'v1','k2':'v2}, 第二种主要是用于发送复杂数据,如复杂字典,值中又包含字典的,案例如下:# django服务器端 def server(request): # request.POST只能处理简单请求,也就是请求头中Content-Type是application/x-www-form-urlencoded;charset=UTF-8 # print(request.POST) # 要处理复杂请求,也就是请求头中Content-Type是application/json;charset=UTF-8 # 需要从request.body中获取,再解码,然后再json.loads print(json.loads(request.body.decode('utf-8'))) return HttpResponse('已接收') # 客户端 import requests import json # 发送简单数据给服务器 # response = requests.post( # url=' http://127.0.0.1:8000/server/', # headers={ # # 默认值,可不设置 # 'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8', # }, # data={'name': 'Treelight', 'age': 18} # ) # print(response.text) # 发送复杂数据比如字典的值只能为列表、数字、字符串等,不可以用字典,如以下的data # 这就请求头中的Content-Type用到application/json格式,同时需要用到json # response = requests.post( # url=' http://127.0.0.1:8000/server/', # headers={ # # 默认值 # 'Content-Type': 'application/json;charset=UTF-8', # }, # data=json.dumps({ # 'disk': ['disk1', 'disk2'], # 'cpu': { # 1: 'cpu1', # 2: 'cpu2', # 3: 'cpu3', # } # }) # ) # print(response.text) # 发送复杂数据的第二种形式 response = requests.post( url=' http://127.0.0.1:8000/server/', json=({ 'disk': ['disk1', 'disk2'], 'cpu': { 1: 'cpu1', 2: 'cpu2', 3: 'cpu3', } }) ) print(response.text)
requests参数
response = requests.request( method='get', # 请求方式 url="", # 请求地址 params = {}, # get形式传的参数 requests.get(url='http://www.cnblogs.com/wupeiqi',params={'id':1,'page':2}) data={}, # 发送的请求体内容, json={}, # 发送的请求体内容,内部json.dumps({....}) headers={}, cookies={}, proxies={}, # 代理、 files = (), #上传文件 auth=None, # 简单常用的验证规则 timeout=(1,2), # 连接超时;响应超时 allow_redirects=True, # 允许重定向 stream=True, # 流式下载 cert=(), # 证书 )
import requests session = requests.Session() ### 1、首先登陆任何页面,获取cookie i1 = session.get(url="http://dig.chouti.com/help/service") ### 2、用户登陆,携带上一次的cookie,后台对cookie中的 gpsd 进行授权 i2 = session.post( url="http://dig.chouti.com/login", data={ 'phone': "8615131255089", 'password': "xxxxxx", 'oneMonth': "" } ) i3 = session.post( url="http://dig.chouti.com/link/vote?linksId=8589623", ) print(i3.text)
更多请参考https://www.cnblogs.com/wupeiqi/articles/5354900.html
请求体:FormData
Request
用户凭证:响应体 cookie中
BeautifulSoup模块
安装:pip install BeautifulSoup4 这个模块是对下载的html或xml内容进行结构化处理的 常用的属性、方法如下:# 把文本传过去,然后用python自带的html解释器处理,进行结构化,返回的是顶级结构 soup = BeautifulSoup(response.text, 'html.parser') # 找到id为i1的标签 tag1 = soup.select('#i1') # 属性的增删改 del tag1.attrs['name'] tag1.attrs['name'] = 'username' # 如果没有此属性则会增加 tag1.attrs['result'] = 'Good' # 找孩子 tag1.children # 找子子孙孙 tag1.descendsants # 删除标签里的所有内容,但不包括自己 tag1.clear # 全部删除,包括自己 tag1.decompose() # 全部删除,包括自己,而且有返回值 ret = tag1.extra() # 转换成字符串 tag1.decode() # 转换成字符串,但不包括自己 tag1.decode_contents() # 转换成字节 tag1.encode() # 转换成字节,但不包括自己 tag1.encode_contents() # 找第一个标签,文本为Beckham tag2 = soup.find(name='div', attrs={'class':'form-control','id':'username'}, recursive=True, text='Beckham') # 和上面效果一样的,但注意class需要有下划线,建议使用上面的 tag2 = soup.find(name='div', class_='form-control',id='username', recursive=True, text='Beckham') # find_all:找到第一个div标签 tag3 = soup.find_all(name='div', limit=1) # find_all要找的属性值可以是列表,以下是找到div或a标签 tag3 = soup.find_all(name=['div', 'a']) # 找到类为form-control或myform的标签 tag3 = soup.find_all(class=['form-control', 'myform']) # 也可为正则 reg = re.compile('form-.*') tag3 = soup.find_all(class=reg) # 也可为函数,找到同时有class和id属性的标签 def func(tag): return tag.has_attr('class') and tag.has_attr('id') v = soup.find_all(name=func) print(v) # 标签是否有此属性 tag3.has_attr('class') # 获取文本 tag3.get_text()
更多请参考https://www.cnblogs.com/wupeiqi/articles/6283017.html
例一、爬取汽车之家的数据
import requests from bs4 import BeautifulSoup # 伪造浏览器,下载页面 # 实例化对象,把所有的内容都封装到对象里 response = requests.get(url="https://www.autohome.com.cn/news/") # 获取状态码 # print(response.status_code) # 获取文本内容, # print(response.text) # 但此文本内容是乱码,因为默认用的是utf8,而此文本用的是gbk # 设置编码 response.encoding = 'gbk' # print(response.text) # 获取二进制格式的文本内容 # print(response.content) # 下载页面完毕 # 结构化处理开始 # 把文本传过去,然后用python自带的html解释器处理,进行结构化,返回的是顶级结构 soup = BeautifulSoup(response.text, 'html.parser') # 找到id是auto-channel-lazyload-article的div 标签,返回的是Tag对象 div = soup.find(name='div', id='auto-channel-lazyload-article') # 从tag对象可继续找其下面的孩子,如找其li,find_all返回的是Tag对象列表 li_list = div.find_all(name='li') for li in li_list: h3 = li.find(name='h3') a = li.find(name='a') p = li.find(name='p') img = li.find(name='img') if not h3: continue # 获取tag对象文本 print(h3.text) # 获取属性 print(a.get('href')) print(p.text) # 下载图片 img_url = 'https:' + img.get('src') filename = img_url.rsplit('/', maxsplit=1)[1] img_res = requests.get(img_url) with open(filename, 'wb') as f: # 需要的是二进制格式 f.write(img_res.content) # 下载图片结构 print('-------------------------------------------------')
Scrapy框架
安装
本人在pycharm中按以下步骤安装暂时没问题。由于要依赖twisted和wheel,: 步骤一、安装pywin32 步骤二、安装wheel 步骤三、安装twisted 步骤四、安装scrapy 下一次尝试一下直接安装scrapy初次使用
使用上感觉上和Django差不多,都要创建项目、创建爬虫、编写内容、启动爬虫 创建项目:scrapy startproject 项目名称 以下操作必须要cd到项目目录文件夹下操作 创建爬虫:scrapy genspider 爬虫名称 域名 编写内容:在“项目名称\spider”中对应的爬虫名称文件编写 启动爬虫:scrapy crawl 爬虫名称 --nolog(没有日记)scrapy爬虫流程
图片来源于https://www.cnblogs.com/wupeiqi/articles/6229292.html
定义起始url处理函数
运行scrapy crawl chouti --nolog,会找到name为chouti的类。然后根据起始url下载网页,下载完成后,会根据此类来定义回调函数处理下载的数据。定义起始urls回调函数的有两种方式# 定义爬虫方式之一、不推荐,不灵活,因为如果有多个起始url,就会交给同一个parse函数处理 class ChoutiSpider(scrapy.Spider): name = 'chouti' allowed_domains = ['chouti.com'] start_urls = ['https://dig.chouti.com/'] def parse(self, response): print(response.text)
from scrapy.http import Request # 定义爬虫处理方式二,推荐,不同的起始url可交给不同的函数处理 class ChoutiSpider(scrapy.Spider): name = 'chouti' allowed_domains = ['chouti.com'] def start_requests(self): # 定义处理url下载完的处理函数,相当于加入图中的任务队列 yield Request(url='https://dig.chouti.com', callback=self.parse_chouti) yield Request(url='https://www.baidu.com', callback=self.parse_baidu) # 相当于以下操作,两都均可 # return [Request(url='https://dig.chouti.com', callback=self.parse_chouti), # Request(url='https://www.baidu.com', callback=self.parse_baidu)] def parse_chouti(self, response): print('处理chouti', response) def parse_baidu(self, response): print('处理baidu', response)
选择器
在scrapy框架中,下载网页会自动完成,但要从网页中抽取数据,还是需要自己手动完成。可使用BeautifulSoup模块,可使用scrapy自带的选择器,这里就介绍一下选择器,先送上例子:def parse_chouti(self, response): """获取抽屉中所有的标题和其对应url""" # response封装了选择器可选择对象 # 以以下为例,如果属性class是模糊匹配,则需要[contains(@class, "值")] # 属性精确匹配,则[@class="link-item"] # //是从根找,.//是从当前为根找子子孙孙,/只找孩子 # 从最根部开始找子孙中class中含有link-item属性的div item_list = response.selector.xpath('//div[contains(@class, "link-item")]') for item in item_list: # 在选择条件后加上/text()获取文本对象,get方法是把提取第一个值 # 相对get方法,也有get_list # 以当前标签作为根,在子孙中查找class中含有link-title的标签 title = item.xpath('.//a[contains(@class, "link-title")]/text()').get() # 在选择条件后加/@属性获取属性值 url = item.xpath('.//a[contains(@class, "link-title")]/@href').get()
知识点:
一、实例化选择器
方式一、response.selector.xpath('选择条件')
方式二、
from scrapy import Selector
tag = Selector(body=body).xpaht('选择条件')
二、搜索起点
//:从最根的目录查找子孙
.//:从当前标签开始找子孙
/:找儿子
三、属性可模糊匹配或精确匹配
1、模糊匹配,则需要[contains(@class, "值")]
2、精确匹配,则[@class="link-item"]
四、查找文本或属性选择器
文本:'选择条件/text()',返回文本选择器
属性:'选择条件/@href',返回href属性选择器
五、提取文本或属性
以上只是找到选择器,但没返回字符串,可通过get、getl_list取
1、get:以字符串形式返回第一个的文本或属性
2、get_list:返回的是列表
六、选择条件中使用正则表达式
格式://标签名[re:test(@属性, "正则表达式")]
如//div[re:test(@class, "^item-\d&")]
Pipelines
有一个pipelines.py文件,此文件里定义了一个类,有什么作用呢?这个文件定义了一个接口,提供了一些函数以供爬虫使用的前、中、后使用,比如: open_spider:在爬虫前调用此函数 close_spider:在爬虫执行完毕调用此函数 process_item:在爬虫回调函数中如果使用yield返回一个item对象,则执行此函数 也提供了以下方法: __init__:这个就不多说了 from_crawler:最先执行的方法,必须要返回一个对象(基本上不用),类的静态方法,需要加上@classmethod 以上方法执行的顺序 from_crawler--->__init__--->open_spider-->process_item-->close_spider 使用示例如下:class DBPipeline(object): def process_item(self, item, spider): """回调函数执行完yield后执行此方法""" return item class FilePipeline(object): def open_spider(self, spider): """在执行爬虫前执行的方法""" self.f = open('log.txt', 'w', encoding='utf-8') def process_item(self, item, spider): """回调函数执行完yield后执行此方法""" self.f.write(item['title'] + '\n') self.f.flush() return item def close_spider(self, spider): """在爬虫执行完毕的方法""" self.f.close()
不过使用这些函数,还需要以下步骤:
一、在settings.py中注册,如下
# 值越小就优先执行 ITEM_PIPELINES = { 's1.pipelines.DBPipeline': 300, 's1.pipelines.FilePipeline': 400, }
二、在items.py中定义item类
import scrapy class S1Item(scrapy.Item): # define the fields for your item here like: # name = scrapy.Field() url = scrapy.Field() title = scrapy.Field()
三、在回调函数中使用yield,返回一个item对象
yield S1Item(title=title, url=url)
关于process_item函数的传递
由于pipelines中可注册多个类,也就是说会有多个process_item执行,它们的执行顺序是按照settings中设置的值执行,越小的越优先,而且首先执行的会传递item给下一个process_item,如下图 ![](https://img2018.cnblogs.com/blog/1503064/202002/1503064-20200211153437070-853001352.png)如果不想传递,则可以这样做
from scrapy.exceptions import DropItem # 导入模块 class DBPipeline(object): def process_item(self, item, spider): """回调函数执行完yield后执行此方法""" # 不传递item了 return DropItem()
settings.py
一、pipelines配置# 值越小就优先执行 ITEM_PIPELINES = { 's1.pipelines.DBPipeline': 300, 's1.pipelines.FilePipeline': 400, }
二、递归层级限制:DEPTH_LIMIT = 2
三、过滤规则设置
DUPEFILTER_CLASS = 's1.dupefilter.RepeatUrl'
四、下载中间件设置
DOWNLOADER_MIDDLEWARES = { 's1.middlewares.S1DownloaderMiddleware': 543, }
五、是否遵循网站规则
ROBOTSTXT_OBEY = True
自定义过滤规则
比如说有一些网页已经爬过了,就不想再爬,可以通过过滤来。是怎么样过滤的呢?其实是把任务放到调度器前,它会经过过滤规则过滤一下看看是否符合规则的。例子如下:class RepeatUrl: def __init__(self): self.visited_url = set() @classmethod def from_settings(cls, settings): """ 初始化时,调用 :param settings: :return: """ return cls() def request_seen(self, request): """ 检测当前请求是否已经被访问过 :param request: :return: True表示已经访问过;False表示未访问过 """ if request.url in self.visited_url: return True self.visited_url.add(request.url) return False def open(self): """ 开始爬去请求时,调用 :return: """ print('open replication') def close(self, reason): """ 结束爬虫爬取时,调用 :param reason: :return: """ print('close replication') def log(self, request, spider): """ 记录日志 :param request: :param spider: :return: """ print('repeat', request.url)
settings.py中
DUPEFILTER_CLASS = 's1.dupefilter.RepeatUrl'
来源https://www.cnblogs.com/wupeiqi/articles/6229292.html
下载中间件
scrapy提供了多个中间件,比较常用的是下载中间件,要使用中间件,要设置settings.pyDOWNLOADER_MIDDLEWARES = { 's1.middlewares.S1DownloaderMiddleware': 543, }
一般有以下的作用,前两个比较常用
1、设置请求头,返回None
2、设置代理,返回None
3、返回Response,流程如下:
4、返回Request,由于会添加到调试器,会不断在中间件和调度器来回流程如下:
5、丢弃请求
from scrapy.exceptions import IgnoreRequest
raise IgnoreRequest
所有示例请看以下例子:
中间件
# -*- coding: utf-8 -*- # Define here the models for your spider middleware # # See documentation in: # https://docs.scrapy.org/en/latest/topics/spider-middleware.html from scrapy import signals class S1SpiderMiddleware(object): # Not all methods need to be defined. If a method is not defined, # scrapy acts as if the spider middleware does not modify the # passed objects. @classmethod def from_crawler(cls, crawler): # This method is used by Scrapy to create your spiders. s = cls() crawler.signals.connect(s.spider_opened, signal=signals.spider_opened) return s def process_spider_input(self, response, spider): # Called for each response that goes through the spider # middleware and into the spider. # Should return None or raise an exception. return None def process_spider_output(self, response, result, spider): # Called with the results returned from the Spider, after # it has processed the response. # Must return an iterable of Request, dict or Item objects. for i in result: yield i def process_spider_exception(self, response, exception, spider): # Called when a spider or process_spider_input() method # (from other spider middleware) raises an exception. # Should return either None or an iterable of Request, dict # or Item objects. pass def process_start_requests(self, start_requests, spider): # Called with the start requests of the spider, and works # similarly to the process_spider_output() method, except # that it doesn’t have a response associated. # Must return only requests (not items). for r in start_requests: yield r def spider_opened(self, spider): spider.logger.info('Spider opened: %s' % spider.name) class S1DownloaderMiddleware(object): # Not all methods need to be defined. If a method is not defined, # scrapy acts as if the downloader middleware does not modify the # passed objects. @classmethod def from_crawler(cls, crawler): # This method is used by Scrapy to create your spiders. s = cls() crawler.signals.connect(s.spider_opened, signal=signals.spider_opened) return s def process_request(self, request, spider): # Called for each request that goes through the downloader # middleware. # Must either: # - return None: continue processing this request # - or return a Response object # - or return a Request object # - or raise IgnoreRequest: process_exception() methods of # installed downloader middleware will be called # 1、请求头处理,所有的请求都会加上 request.headers['User-Agent'] = 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36' # 2、添加代理 # 3、返回响应 # from scrapy.http import Response # return Response(url='www.abc.com', body=b'request failed') # 4、 返回request,这样会不断地在下载中间件和调试器来回 # from scrapy.http import Request # return Request(url='dig.chouti.com') # 5、丢弃请求 # from scrapy.exceptions import IgnoreRequest # raise IgnoreRequest def process_response(self, request, response, spider): # Called with the response returned from the downloader. # Must either; # - return a Response object # - return a Request object # - or raise IgnoreRequest return response def process_exception(self, request, exception, spider): # Called when a download handler or a process_request() # (from other downloader middleware) raises an exception. # Must either: # - return None: continue processing this exception # - return a Response object: stops process_exception() chain # - return a Request object: stops process_exception() chain pass def spider_opened(self, spider): spider.logger.info('Spider opened: %s' % spider.name)
代理
实现方式有两种 一、内置 在start_request中加入以下语句import os os.environ['http_proxy'] = "http://root:woshiniba@192.168.11.11:9999/" os.environ['https_proxy'] = "http://192.168.11.11:9999/"
二、下载中间件中实现
代理中间件
import random import base64 import six def to_bytes(text, encoding=None, errors='strict'): if isinstance(text, bytes): return text if not isinstance(text, six.string_types): raise TypeError('to_bytes must receive a unicode, str or bytes ' 'object, got %s' % type(text).__name__) if encoding is None: encoding = 'utf-8' return text.encode(encoding, errors) class ProxyMiddleware(object): def process_request(self, request, spider): PROXIES = [ {'ip_port': '111.11.228.75:80', 'user_pass': ''}, {'ip_port': '120.198.243.22:80', 'user_pass': ''}, {'ip_port': '111.8.60.9:8123', 'user_pass': ''}, {'ip_port': '101.71.27.120:80', 'user_pass': ''}, {'ip_port': '122.96.59.104:80', 'user_pass': ''}, {'ip_port': '122.224.249.122:8088', 'user_pass': ''}, ] proxy = random.choice(PROXIES) if proxy['user_pass'] is not None: request.meta['proxy'] = to_bytes("http://%s" % proxy['ip_port']) encoded_user_pass = base64.encodestring(to_bytes(proxy['user_pass'])) request.headers['Proxy-Authorization'] = to_bytes('Basic ' + encoded_user_pass) else: request.meta['proxy'] = to_bytes("http://%s" % proxy['ip_port'])
扩展
自定义扩展时,利用信号在指定位置注册制定操作自定义扩展
from scrapy import signals class MyExtension(object): def __init__(self, value): self.value = value @classmethod def from_crawler(cls, crawler): val = crawler.settings.getint('MMMM') ext = cls(val) crawler.signals.connect(ext.spider_opened, signal=signals.spider_opened) crawler.signals.connect(ext.spider_closed, signal=signals.spider_closed) return ext def spider_opened(self, spider): print('open') def spider_closed(self, spider): print('close')
自定制命令
在spiders同级创建任意目录,如:commands 在其中创建 crawlall.py 文件 (此处文件名就是自定义的命令)自定制命令
from scrapy.commands import ScrapyCommand from scrapy.utils.project import get_project_settings class Command(ScrapyCommand): requires_project = True def syntax(self): return '[options]' def short_desc(self): return 'Runs all of the spiders' def run(self, args, opts): spider_list = self.crawler_process.spiders.list() for name in spider_list: self.crawler_process.crawl(name, **opts.__dict__) self.crawler_process.start()