爬虫
爬虫
什么是爬虫?就是伪造浏览器,下载网页源代码,再从源代码获取需要的数据,一般分为两步: 1、伪造浏览器下载网页(requests模块) 2、对网页的内容进行结构化处理(BeautifulSoup模块)requests模块
安装:pip install requests 下面是这个模块常用到的方法、属性1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | # 实例化对象,把所有的内容都封装到对象里,以下是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}, 第二种主要是用于发送复杂数据,如复杂字典,值中又包含字典的,案例如下:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | # 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参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | 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 = (), # 证书 ) |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | 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内容进行结构化处理的 常用的属性、方法如下:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | # 把文本传过去,然后用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
例一、爬取汽车之家的数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | 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 下面是这个模块常用到的方法、属性1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | # 实例化对象,把所有的内容都封装到对象里,以下是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}, 第二种主要是用于发送复杂数据,如复杂字典,值中又包含字典的,案例如下:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | # 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参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | 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 = (), # 证书 ) |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | 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内容进行结构化处理的 常用的属性、方法如下:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | # 把文本传过去,然后用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
例一、爬取汽车之家的数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | 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回调函数的有两种方式1 2 3 4 5 6 7 8 | # 定义爬虫方式之一、不推荐,不灵活,因为如果有多个起始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) |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | 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自带的选择器,这里就介绍一下选择器,先送上例子:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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 使用示例如下:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | 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中注册,如下
1 2 3 4 5 | # 值越小就优先执行 ITEM_PIPELINES = { 's1.pipelines.DBPipeline' : 300 , 's1.pipelines.FilePipeline' : 400 , } |
二、在items.py中定义item类
1 2 3 4 5 6 7 8 | 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对象
1 | yield S1Item(title = title, url = url) |
关于process_item函数的传递
由于pipelines中可注册多个类,也就是说会有多个process_item执行,它们的执行顺序是按照settings中设置的值执行,越小的越优先,而且首先执行的会传递item给下一个process_item,如下图 如果不想传递,则可以这样做
1 2 3 4 5 6 7 8 | from scrapy.exceptions import DropItem # 导入模块 class DBPipeline( object ): def process_item( self , item, spider): """回调函数执行完yield后执行此方法""" # 不传递item了 return DropItem() |
settings.py
一、pipelines配置1 2 3 4 5 | # 值越小就优先执行 ITEM_PIPELINES = { 's1.pipelines.DBPipeline' : 300 , 's1.pipelines.FilePipeline' : 400 , } |
二、递归层级限制:DEPTH_LIMIT = 2
三、过滤规则设置
1 | DUPEFILTER_CLASS = 's1.dupefilter.RepeatUrl' |
四、下载中间件设置
1 2 3 | DOWNLOADER_MIDDLEWARES = { 's1.middlewares.S1DownloaderMiddleware' : 543 , } |
五、是否遵循网站规则
1 | ROBOTSTXT_OBEY = True |
自定义过滤规则
比如说有一些网页已经爬过了,就不想再爬,可以通过过滤来。是怎么样过滤的呢?其实是把任务放到调度器前,它会经过过滤规则过滤一下看看是否符合规则的。例子如下:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | 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中
1 | DUPEFILTER_CLASS = 's1.dupefilter.RepeatUrl' |
来源https://www.cnblogs.com/wupeiqi/articles/6229292.html
下载中间件
scrapy提供了多个中间件,比较常用的是下载中间件,要使用中间件,要设置settings.py1 2 3 | DOWNLOADER_MIDDLEWARES = { 's1.middlewares.S1DownloaderMiddleware' : 543 , } |
一般有以下的作用,前两个比较常用
1、设置请求头,返回None
2、设置代理,返回None
3、返回Response,流程如下:
4、返回Request,由于会添加到调试器,会不断在中间件和调度器来回流程如下:
5、丢弃请求
from scrapy.exceptions import IgnoreRequest
raise IgnoreRequest
所有示例请看以下例子:
中间件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 | # -*- 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中加入以下语句1 2 3 | import os os.environ[ 'http_proxy' ] = "http://root:woshiniba@192.168.11.11:9999/" os.environ[ 'https_proxy' ] = "http://192.168.11.11:9999/" |
二、下载中间件中实现
代理中间件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | 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' ]) |
扩展
自定义扩展时,利用信号在指定位置注册制定操作自定义扩展
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | 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 文件 (此处文件名就是自定义的命令)自定制命令
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | 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() |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 上周热点回顾(3.3-3.9)
· AI 智能体引爆开源社区「GitHub 热点速览」