爬虫技术:scrapy 知识点一
---恢复内容开始---
1.scrapy框架
每一步的解释:
step1:引擎从爬虫器获取要爬行的初始请求。
step2:引擎在调度程序中调度请求,引擎把这个初始请求传递给调度器,并向调度器索要下一个请求。
step3:调度程序将下一个请求返回给引擎。
step4:引擎通过下载器中间件将请求发送给下载器。
step5:一旦页面下载完成,下载器就会生成一个响应(带有该页面)并将通过下载器中间件响应发送到引擎。
step6:引擎接收来自下载器的响应,并通过Spider中间件将其发送给Spider进行处理。
step7:爬行器处理响应,并通过爬行器中间件将抓取的数据和新请求(要跟踪的)返回给引擎
step8:引擎将处理过的数据发送到数据管道,然后将处理过的请求发送到调度程序,并请求可能的下一个要爬行的请求。
step9:流程重复(从步骤1开始),直到调度程序不再发出请求。
每一个元件的作用:
引擎 Engine:负责控制系统所有组件之间的数据流,并在某些操作发生时触发事件
调度器:调度器接收来自引擎的请求,并对它们进行排队,以便稍后在引擎请求它们时将请求对象提供给引擎。
下载器:下载器负责获取web页面并将响应内容提供给引擎,而引擎又将响应内容提供给爬虫器。
爬虫器:spider是由Scrapy用户编写的自定义类,用于解析响应并从响应或后续请求中提取数据(也称为抓取数据)。
管道:项目管道负责处理爬虫提取(或处理)后的数据。典型的任务包括清理数据、验证和持久性(比如在数据库中存储)。
下载器中间件:Downloader中间件是位于引擎和Downloader之间的特定钩子,当请求从引擎传递到Downloader以及响应从Downloader传递到引擎时,处理引擎和下载器中间的请求和响应,并进行传递。
爬虫中间件:Spider中间件是位于引擎和Spider之间的特定钩子,能够处理Spider输入(响应)和输出(数据和请求)
内容翻译于:https://doc.scrapy.org/en/master/topics/architecture.html
scrapy框架:Scrapy是用Twisted编写的,Twisted是一种流行的Python事件驱动的网络框架。因此,它使用非阻塞(即异步)代码实现并发。
Frequently Asked Questions:框架的常见问题
1.Scrapy可以使用HTTP代理吗?
是的。通过HTTP代理下载器中间件提供了对HTTP代理的支持(因为Scrapy 0.8)。具体见:HttpProxyMiddleware。
使用方式一:在中间件,middlewares.py中添加如下代码。 class ProxyMiddleware(object):
def process_request(self,request,spider):
if request.url.startswith("http://"):
request.meta['proxy']="http://"+'127.0.0.0:8000' # http代理
elif request.url.startswith("https://"):
request.meta['proxy']="https://"+'127.0.0.0:8000' # https代理 然后在setting.py中的middleware中设置 # Enable or disable downloader middlewares # See https://doc.scrapy.org/en/latest/topics/downloader-middleware.html
DOWNLOADER_MIDDLEWARES = { 'biquge.middlewares.ProxyMiddleware': 100, }
使用方式二:在scarpy的爬虫代码中进行编写,重写start_request()函数,通过meta字典传递代理。 import scrapy class ProxySpider(scrapy.Spider): name = 'proxy' allowed_domains = ["httpbin.org"] def start_requests(self): url = 'http://httpbin.org/get' proxy = '127.0.0.0:8000' proxies = "" if url.startswith("http://"): proxies = "http://"+str(proxy) elif url.startswith("https://"): proxies = "https://"+str(proxy) #注意这里面的meta={'proxy':proxies},一定要是proxy进行携带,其它的不行,后面的proxies一定 要是字符串,其它任何形式都不行 yield scrapy.Request(url, callback=self.parse,meta={'proxy':proxies}) def parse(self,response): print(response.text)
CrawlSpider类
rules:
它是一个(或多个)规则对象的列表。每个规则都定义了爬行站点的特定行为。规则对象描述如下。如果多个规则匹配相同的链接,则根据在此属性中定义规则的顺序使用第一个规则。有自动去重url的功能
Rule(link_extractor, callback=None, cb_kwargs=None, follow=None, process_links=None, process_request=None)
百度百科案例:深度爬取
# -*- coding: utf-8 -*- import lxml.etree import scrapy from scrapy.spiders import CrawlSpider,Rule #提取超链接的规则 from scrapy.linkextractors import LinkExtractor # 提取超链接 from bidubaike.items import BidubaikeItem class BaidubaikeSpider(CrawlSpider): # scrapy.Spider name = 'baidubaike' # 规则匹配的url样式,https://baike.baidu.com/item/%E5%A5%BD%E5%A5%BD%E8%AF%B4%E8%AF%9D/20361348?secondId=165077&mediaId=mda-hcssahwn1h3mk6zz' # allowed_domains = ['https://baike.baidu.com/'] # 这里需要关闭这个功能 # 或者设置为下面形式 allowed_domins = ['https://baike.baidu.com/item/'] start_urls = ['https://baike.baidu.com/item/Python/407313'] # 页面提取超链接 TODO 规则定义为每一页的url,也可以实现翻页 pagelinks = LinkExtractor(allow=(r"/item/.*")) # 提取规则 # follow表示是否一致循环下去 rules = [Rule(pagelinks,callback="parse_item",follow=True)] # 返回链接的urllist # 这里不能用原生的def parse()方法,需要换一个名称,要不不能实现 def parse_item(self, response): html = response.body.decode("utf-8") item = BidubaikeItem() e = lxml.etree.HTML(html) if html != None: title1 = e.xpath("//dd[@class='lemmaWgt-lemmaTitle-title']//h1/text()") if len(title1) == 0: part_a = "" else: part_a = title1[0] title2 = e.xpath("//dd[@class='lemmaWgt-lemmaTitle-title']//h2/text()") if len(title2) == 0: part_b = "" else: part_b = title2[0] title = part_a + part_b title += "\r\n" item["title"] = title item["url"] = response.url yield item else: item["title"] = None item["url"] = None yield item
配置随机代理的方式:
方式一:
setting.py中设置: PROXIES = [ "http://120.84.102.21:9999", "http://114.239.150.233:9999", "http://112.87.71.174:9999" ]
在middleware.py中创建一个类 class ProxyMiddleWare(object): def __init__(self,ip): self.ip = ip # 列表 @classmethod def from_crawler(cls, crawler): # 从setting.py中得到PORXIES列表 return cls(ip=crawler.setting.get("PORXIES")) # 列表 def process_request(self,request,spider): ip = random.choice(self.ip) # 列表中随机选值 request.meta["proxy"] = ip # 给请求对象加上代理参数
setting.py中开启DOWNLOADER_MIDDLEWARES 并将middlewares.py中创建的类添加进去。 DOWNLOADER_MIDDLEWARES = { 'Proxy.middlewares.ProxyDownloaderMiddleware': 543, 'Proxy.middlewares.ProxyMiddleWare':543, }
scrapy登录操作:无验证类
# -*- coding: utf-8 -*- import scrapy class RenrenloginSpider(scrapy.Spider): name = 'renrenlogin' allowed_domains = ['www.renren.com'] start_urls = ['http://www.renren.com/SysHome.do'] # 人人登录界面网址 def parse(self, response): """表单登录,发送用户名和验证码""" yield scrapy.FormRequest.from_response(response, formdata={"email":"xxx","password":"xxx"}, # 这里scarpy会自动模拟js对email和passowrd进行加密 callback = self.parse_person_homepage) def parse_person_homepage(self, response): homepage_url = "http://www.renren.com/xxx/profile" yield scrapy.Request(url=homepage_url,callback=self.parse_user_info) def parse_user_info(self,response): html = response.body with open("renren.html","w") as f: f.write(html.decode("gbk","ignore"))
scrapy登录操作:无验证类 --- 利用cookie跳转到其他页面
# -*- coding: utf-8 -*- import scrapy class RenrenloginSpider(scrapy.Spider): name = 'csdnlogin.py' allowed_domains = ['www.csdn.net'] start_urls = ['https://passport.csdn.net/guide']
# 手动登录后获取到cookies,利用fidder抓取到cookies包,进行请求。 cookies = { "uuid_tt_dd": "xxx", "dc_session_id": "xxx", "smidV2": "xxx", "UN": "xxx", "Hm_ct_6bcd52f51e9b3dce32bec4a3997715ac": "xxx", "Hm_lvt_6bcd52f51e9b3dce32bec4a3997715ac": "xxx", "aliyun_webUmidToken": "xxx", "MSG-SESSION": "xxx", "SESSION": "xxx", "dc_tos": "xxx", "UserName": "xxx", "UserInfo": "xxx", "UserToken": "xxx", "UserNick": "xxx", "AU": "xxx", "BT": "xxx", "p_uid": "xxx", "Hm_lpvt_6bcd52f51e9b3dce32bec4a3997715ac": "xxx" } def start_requests(self): for url in self.start_urls: yield scrapy.FormRequest(url=url,cookies=self.cookies,callback=self.parse_newpage) def parse_newpage(self,response): with open("csdn.html","wb") as f: f.write(response.body)
报错:Connection to the other side was lost in a non-clean fashion. 爬取问政的时候
构造url_list进行爬取---阳光问政
import re import lxml.etree import scrapy from sun.items import SunItem class SuninfoSpider(scrapy.Spider): name = 'suninfo' allowed_domains = ['wz.sun0769.com'] start_urls = ['http://wz.sun0769.com/index.php/question/report?page=0'] def parse(self, response): html_str = response.body.decode("gbk",errors="ignore") e = lxml.etree.HTML(html_str) count_str = e.xpath("//div[@class='pagination']/text()")[-1] # 获取帖子数量 count = re.findall("(\d+)", count_str)[0] page_count = int(count) // 30 url_list = list() url = "http://wz.sun0769.com/index.php/question/report?page={}" for i in range(0, page_count + 1): url_list.append(url.format(i * 30)) # 测试10页的数据 for url in url_list[:10]: yield scrapy.Request(url=url,callback=self.parse_page_info) def parse_page_info(self,response): html_str = response.body.decode("gbk",errors="ignore") e = lxml.etree.HTML(html_str) item = SunItem() tr_list = e.xpath("//div[@class='newsHead clearfix']//table[2]//tr") for tr in tr_list: item["id"] = tr.xpath(".//td[1]/text()")[0] item["title"] = tr.xpath(".//td[3]//a[1]/text()")[0] person = tr.xpath(".//td[5]/text()") if len(person) == 0: person = "MISS" item["name"] = person else: person = tr.xpath(".//td[5]/text()")[0] item["name"] = person item["time"] = tr.xpath(".//td[6]/text()")[0] yield item
自动抓取页面链接爬取---阳光问政
# -*- coding: utf-8 -*- import re import lxml.etree import scrapy from sun.items import SunItem from scrapy.spiders import CrawlSpider, Rule from scrapy.linkextractors import LinkExtractor class SuninfoSpider(CrawlSpider): name = 'suninfo' # allowed_domains = ['wz.sun0769.com'] start_urls = ['http://wz.sun0769.com/index.php/question/report?page=0'] # http://wz.sun0769.com/index.php/question/report?page=30" # 提取规则的正则是由Linkextractor进行设置的 pagelink = LinkExtractor(allow=(r"page=.*")) rules = [Rule(pagelink, callback="parse_item", follow=True)] def parse_item(self, response): html_str = response.body.decode("gbk", errors="ignore") e = lxml.etree.HTML(html_str) item = SunItem() tr_list = e.xpath("//div[@class='newsHead clearfix']//table[2]//tr") for tr in tr_list: item["id"] = tr.xpath(".//td[1]/text()")[0] item["title"] = tr.xpath(".//td[3]//a[1]/text()")[0] person = tr.xpath(".//td[5]/text()") if len(person) == 0: person = "MISS" item["name"] = person else: person = tr.xpath(".//td[5]/text()")[0] item["name"] = person item["time"] = tr.xpath(".//td[6]/text()")[0] yield item
scary框架的去重原理:
第一步:框架工具包中的scrapy/utils/request.py
request_fingerprint函数---对请求对象进行签名--sha1
怎么判断请求对象是同一个呢,签名时候,对请求对象的 method,url,body(包含请求头)进行加密
请求体:request.body样子是什么呢? # TODO
sha1加密:
from hashlib import sha1 fp = sha1() str1 = "a" str2 = "b" str3 = "c" fp.update(str1.encode()) fp.update(str2.encode()) fp.update(str2.encode())
# 理解为:对str1 + str2 + str3 = "abc" 进行加密,因为sha1对任何长度的二进制数据加密后都得到40位的16进制字符串。 ret = fp.hexdigest() print(ret) c64d3fcde20c5cd03142171e5ac47a87aa3c8ace # 生成一个40位长度的16进制的字符串。
第二步:去重工具:scrapy/dupefilters.py
集合(防止指纹(长度为40的字符串)) set(指纹1,指纹2,指纹3,。。。。,)
request_seen---看见函数,对指纹进行处理,
指纹在集合就直接返回,不进行下一步,return True
指纹不在集合,就加入集合,再进行下一步。
第三步:scrapy/core/scheduler.py/enqueue_request 调度器
enqueue_request 函数中:request对象的 dont_fliter=false且当前request对象的签名在集合中。那么就将当前的请求对象放入请求队列中去。
具体代码分析:https://blog.csdn.net/Mr__lqy/article/details/85859361
输出log日志
settings中 LOG_FILE = "0769.log" LOG_LEVEL = "DEBUG" 控制台不做任何输出,结果都写在名为0769的日志文件中。
scrapy抓取json数据
图片下载保存方法一:urllib.request
import urllib.request class DouyumeinvPipeline(object): def process_item(self, item, spider): url = item["imgurl"] name = item["id"] urllib.request.urlretrieve(url,"pic/{}.jpg".format(name)) # 直接使用urllib.request中的urlretrieve函数,直接将请求的资源保存指定路径 return item
图片下载保存方法二:Imagepipeline
# setting.py中设置: ITEM_PIPELINES = { 'douyuimage.pipelines.ImagePipeLine': 1, }
# 实际上图片是全部默认保存在full文件夹中,full文件夹则保存在IMAGE_STORE这个路径下。 IMAGES_STORE = r'C:\Users\Administrator\Desktop\img' IMAGES_EXPIRES = 90 # pipelines.py中设置: class ImagePipeLine(ImagesPipeline): def get_media_requests(self, item, info):
# 每个item都会经过这个函数,然后这个函数在发起一个请求对象,将结果传给 item_completed函数 url = item["img_url"] yield scrapy.Request(url) def item_completed(self, results, item, info): # results = [(True,{"path":"full/xxxxxx.jpg"})] image_path = [x['path'] for ok, x in results if ok] # 仅仅是图片路径是:full/xxxx.jpg if not image_path: raise DouyuimageItem("Item contains no images") item['image_path'] = image_path return item # 在items中设置: class DouyuimageItem(scrapy.Item): # define the fields for your item here like: # name = scrapy.Field() id = scrapy.Field() name = scrapy.Field() img_url = scrapy.Field() image_path = scrapy.Field() pass # spider.py中设置: class ImgdownSpider(scrapy.Spider): name = 'imgdown' allowed_domains = ['www.douyu.com'] current_page = 1 start_urls = ['https://www.douyu.com/gapi/rknc/directory/yzRec/{}'.format(current_page)] def parse(self, response): item = DouyuimageItem() data = json.loads(response.body.decode())["data"] info_dict_list = data["rl"] for info_dict in info_dict_list: item["id"] = info_dict["rid"] item["name"] = info_dict["nn"] item["img_url"] = info_dict["rs16"] yield item # 运行的结果 {'id': 6837192, 'image_path': ['full/e63847f2205b92e0a6c46c17d68e37f708948337.jpg'], 'img_url': 'https://rpic.douyucdn.cn/asrpic/190916/6837192_6066111_26880_2_1546.jpg', 'name': '十三金丶'}
scrapy翻页抓取数据
翻页抓取数据方式一:页码递增法
import requests from bs4 import BeautifulSoup url = "https://blog.csdn.net/itcastcpp/article/list/1?" html_content = requests.get(url).content soup = BeautifulSoup(html_content,"html5lib") # 将网页的字节数据解析为html5的类型 div_list = soup.find_all("div",class_="article-item-box csdn-tracking-statistics") # 按照class的属性进行寻找。 for div in div_list: title = (div.find_all("h4")[0]).find_all("a")[0] # 获取h4下的a标签的文本值 print(title.get_text()) # 结果: 原 兄弟连区块链 Go 学习大纲-取得大纲试看视频联系微信yinchengak48 原 尹成学院golang学习快速笔记(2)表达式 原 尹成学院golang学习快速笔记(1)类型 原 区块链交易所基础开发(1)通过接口查询区块链各个币种的提币情况-ada 原 Golang精编100题-搞定golang面试
---恢复内容结束---
---恢复内容开始---
1.scrapy框架
每一步的解释:
step1:引擎从爬虫器获取要爬行的初始请求。
step2:引擎在调度程序中调度请求,引擎把这个初始请求传递给调度器,并向调度器索要下一个请求。
step3:调度程序将下一个请求返回给引擎。
step4:引擎通过下载器中间件将请求发送给下载器。
step5:一旦页面下载完成,下载器就会生成一个响应(带有该页面)并将通过下载器中间件响应发送到引擎。
step6:引擎接收来自下载器的响应,并通过Spider中间件将其发送给Spider进行处理。
step7:爬行器处理响应,并通过爬行器中间件将抓取的数据和新请求(要跟踪的)返回给引擎
step8:引擎将处理过的数据发送到数据管道,然后将处理过的请求发送到调度程序,并请求可能的下一个要爬行的请求。
step9:流程重复(从步骤1开始),直到调度程序不再发出请求。
每一个元件的作用:
引擎 Engine:负责控制系统所有组件之间的数据流,并在某些操作发生时触发事件
调度器:调度器接收来自引擎的请求,并对它们进行排队,以便稍后在引擎请求它们时将请求对象提供给引擎。
下载器:下载器负责获取web页面并将响应内容提供给引擎,而引擎又将响应内容提供给爬虫器。
爬虫器:spider是由Scrapy用户编写的自定义类,用于解析响应并从响应或后续请求中提取数据(也称为抓取数据)。
管道:项目管道负责处理爬虫提取(或处理)后的数据。典型的任务包括清理数据、验证和持久性(比如在数据库中存储)。
下载器中间件:Downloader中间件是位于引擎和Downloader之间的特定钩子,当请求从引擎传递到Downloader以及响应从Downloader传递到引擎时,处理引擎和下载器中间的请求和响应,并进行传递。
爬虫中间件:Spider中间件是位于引擎和Spider之间的特定钩子,能够处理Spider输入(响应)和输出(数据和请求)
内容翻译于:https://doc.scrapy.org/en/master/topics/architecture.html
scrapy框架:Scrapy是用Twisted编写的,Twisted是一种流行的Python事件驱动的网络框架。因此,它使用非阻塞(即异步)代码实现并发。
Frequently Asked Questions:框架的常见问题
1.Scrapy可以使用HTTP代理吗?
是的。通过HTTP代理下载器中间件提供了对HTTP代理的支持(因为Scrapy 0.8)。具体见:HttpProxyMiddleware。
使用方式一:在中间件,middlewares.py中添加如下代码。 class ProxyMiddleware(object):
def process_request(self,request,spider):
if request.url.startswith("http://"):
request.meta['proxy']="http://"+'127.0.0.0:8000' # http代理
elif request.url.startswith("https://"):
request.meta['proxy']="https://"+'127.0.0.0:8000' # https代理 然后在setting.py中的middleware中设置 # Enable or disable downloader middlewares # See https://doc.scrapy.org/en/latest/topics/downloader-middleware.html
DOWNLOADER_MIDDLEWARES = { 'biquge.middlewares.ProxyMiddleware': 100, }
使用方式二:在scarpy的爬虫代码中进行编写,重写start_request()函数,通过meta字典传递代理。 import scrapy class ProxySpider(scrapy.Spider): name = 'proxy' allowed_domains = ["httpbin.org"] def start_requests(self): url = 'http://httpbin.org/get' proxy = '127.0.0.0:8000' proxies = "" if url.startswith("http://"): proxies = "http://"+str(proxy) elif url.startswith("https://"): proxies = "https://"+str(proxy) #注意这里面的meta={'proxy':proxies},一定要是proxy进行携带,其它的不行,后面的proxies一定 要是字符串,其它任何形式都不行 yield scrapy.Request(url, callback=self.parse,meta={'proxy':proxies}) def parse(self,response): print(response.text)
CrawlSpider类
rules:
它是一个(或多个)规则对象的列表。每个规则都定义了爬行站点的特定行为。规则对象描述如下。如果多个规则匹配相同的链接,则根据在此属性中定义规则的顺序使用第一个规则。有自动去重url的功能
Rule(link_extractor, callback=None, cb_kwargs=None, follow=None, process_links=None, process_request=None)
百度百科案例:深度爬取
# -*- coding: utf-8 -*- import lxml.etree import scrapy from scrapy.spiders import CrawlSpider,Rule #提取超链接的规则 from scrapy.linkextractors import LinkExtractor # 提取超链接 from bidubaike.items import BidubaikeItem class BaidubaikeSpider(CrawlSpider): # scrapy.Spider name = 'baidubaike' # 规则匹配的url样式,https://baike.baidu.com/item/%E5%A5%BD%E5%A5%BD%E8%AF%B4%E8%AF%9D/20361348?secondId=165077&mediaId=mda-hcssahwn1h3mk6zz' # allowed_domains = ['https://baike.baidu.com/'] # 这里需要关闭这个功能 # 或者设置为下面形式 allowed_domins = ['https://baike.baidu.com/item/'] start_urls = ['https://baike.baidu.com/item/Python/407313'] # 页面提取超链接 TODO 规则定义为每一页的url,也可以实现翻页 pagelinks = LinkExtractor(allow=(r"/item/.*")) # 提取规则 # follow表示是否一致循环下去 rules = [Rule(pagelinks,callback="parse_item",follow=True)] # 返回链接的urllist # 这里不能用原生的def parse()方法,需要换一个名称,要不不能实现 def parse_item(self, response): html = response.body.decode("utf-8") item = BidubaikeItem() e = lxml.etree.HTML(html) if html != None: title1 = e.xpath("//dd[@class='lemmaWgt-lemmaTitle-title']//h1/text()") if len(title1) == 0: part_a = "" else: part_a = title1[0] title2 = e.xpath("//dd[@class='lemmaWgt-lemmaTitle-title']//h2/text()") if len(title2) == 0: part_b = "" else: part_b = title2[0] title = part_a + part_b title += "\r\n" item["title"] = title item["url"] = response.url yield item else: item["title"] = None item["url"] = None yield item
配置随机代理的方式:
方式一:
setting.py中设置: PROXIES = [ "http://120.84.102.21:9999", "http://114.239.150.233:9999", "http://112.87.71.174:9999" ]
在middleware.py中创建一个类 class ProxyMiddleWare(object): def __init__(self,ip): self.ip = ip # 列表 @classmethod def from_crawler(cls, crawler): # 从setting.py中得到PORXIES列表 return cls(ip=crawler.setting.get("PORXIES")) # 列表 def process_request(self,request,spider): ip = random.choice(self.ip) # 列表中随机选值 request.meta["proxy"] = ip # 给请求对象加上代理参数
setting.py中开启DOWNLOADER_MIDDLEWARES 并将middlewares.py中创建的类添加进去。 DOWNLOADER_MIDDLEWARES = { 'Proxy.middlewares.ProxyDownloaderMiddleware': 543, 'Proxy.middlewares.ProxyMiddleWare':543, }
scrapy登录操作:无验证类
# -*- coding: utf-8 -*- import scrapy class RenrenloginSpider(scrapy.Spider): name = 'renrenlogin' allowed_domains = ['www.renren.com'] start_urls = ['http://www.renren.com/SysHome.do'] # 人人登录界面网址 def parse(self, response): """表单登录,发送用户名和验证码""" yield scrapy.FormRequest.from_response(response, formdata={"email":"xxx","password":"xxx"}, # 这里scarpy会自动模拟js对email和passowrd进行加密 callback = self.parse_person_homepage) def parse_person_homepage(self, response): homepage_url = "http://www.renren.com/xxx/profile" yield scrapy.Request(url=homepage_url,callback=self.parse_user_info) def parse_user_info(self,response): html = response.body with open("renren.html","w") as f: f.write(html.decode("gbk","ignore"))
scrapy登录操作:无验证类 --- 利用cookie跳转到其他页面
# -*- coding: utf-8 -*- import scrapy class RenrenloginSpider(scrapy.Spider): name = 'csdnlogin.py' allowed_domains = ['www.csdn.net'] start_urls = ['https://passport.csdn.net/guide']
# 手动登录后获取到cookies,利用fidder抓取到cookies包,进行请求。 cookies = { "uuid_tt_dd": "xxx", "dc_session_id": "xxx", "smidV2": "xxx", "UN": "xxx", "Hm_ct_6bcd52f51e9b3dce32bec4a3997715ac": "xxx", "Hm_lvt_6bcd52f51e9b3dce32bec4a3997715ac": "xxx", "aliyun_webUmidToken": "xxx", "MSG-SESSION": "xxx", "SESSION": "xxx", "dc_tos": "xxx", "UserName": "xxx", "UserInfo": "xxx", "UserToken": "xxx", "UserNick": "xxx", "AU": "xxx", "BT": "xxx", "p_uid": "xxx", "Hm_lpvt_6bcd52f51e9b3dce32bec4a3997715ac": "xxx" } def start_requests(self): for url in self.start_urls: yield scrapy.FormRequest(url=url,cookies=self.cookies,callback=self.parse_newpage) def parse_newpage(self,response): with open("csdn.html","wb") as f: f.write(response.body)
报错:Connection to the other side was lost in a non-clean fashion. 爬取问政的时候
构造url_list进行爬取---阳光问政
import re import lxml.etree import scrapy from sun.items import SunItem class SuninfoSpider(scrapy.Spider): name = 'suninfo' allowed_domains = ['wz.sun0769.com'] start_urls = ['http://wz.sun0769.com/index.php/question/report?page=0'] def parse(self, response): html_str = response.body.decode("gbk",errors="ignore") e = lxml.etree.HTML(html_str) count_str = e.xpath("//div[@class='pagination']/text()")[-1] # 获取帖子数量 count = re.findall("(\d+)", count_str)[0] page_count = int(count) // 30 url_list = list() url = "http://wz.sun0769.com/index.php/question/report?page={}" for i in range(0, page_count + 1): url_list.append(url.format(i * 30)) # 测试10页的数据 for url in url_list[:10]: yield scrapy.Request(url=url,callback=self.parse_page_info) def parse_page_info(self,response): html_str = response.body.decode("gbk",errors="ignore") e = lxml.etree.HTML(html_str) item = SunItem() tr_list = e.xpath("//div[@class='newsHead clearfix']//table[2]//tr") for tr in tr_list: item["id"] = tr.xpath(".//td[1]/text()")[0] item["title"] = tr.xpath(".//td[3]//a[1]/text()")[0] person = tr.xpath(".//td[5]/text()") if len(person) == 0: person = "MISS" item["name"] = person else: person = tr.xpath(".//td[5]/text()")[0] item["name"] = person item["time"] = tr.xpath(".//td[6]/text()")[0] yield item
自动抓取页面链接爬取---阳光问政
# -*- coding: utf-8 -*- import re import lxml.etree import scrapy from sun.items import SunItem from scrapy.spiders import CrawlSpider, Rule from scrapy.linkextractors import LinkExtractor class SuninfoSpider(CrawlSpider): name = 'suninfo' # allowed_domains = ['wz.sun0769.com'] start_urls = ['http://wz.sun0769.com/index.php/question/report?page=0'] # http://wz.sun0769.com/index.php/question/report?page=30" # 提取规则的正则是由Linkextractor进行设置的 pagelink = LinkExtractor(allow=(r"page=.*")) rules = [Rule(pagelink, callback="parse_item", follow=True)] def parse_item(self, response): html_str = response.body.decode("gbk", errors="ignore") e = lxml.etree.HTML(html_str) item = SunItem() tr_list = e.xpath("//div[@class='newsHead clearfix']//table[2]//tr") for tr in tr_list: item["id"] = tr.xpath(".//td[1]/text()")[0] item["title"] = tr.xpath(".//td[3]//a[1]/text()")[0] person = tr.xpath(".//td[5]/text()") if len(person) == 0: person = "MISS" item["name"] = person else: person = tr.xpath(".//td[5]/text()")[0] item["name"] = person item["time"] = tr.xpath(".//td[6]/text()")[0] yield item
scary框架的去重原理:
第一步:框架工具包中的scrapy/utils/request.py
request_fingerprint函数---对请求对象进行签名--sha1
怎么判断请求对象是同一个呢,签名时候,对请求对象的 method,url,body(包含请求头)进行加密
请求体:request.body样子是什么呢? # TODO
sha1加密:
from hashlib import sha1 fp = sha1() str1 = "a" str2 = "b" str3 = "c" fp.update(str1.encode()) fp.update(str2.encode()) fp.update(str2.encode())
# 理解为:对str1 + str2 + str3 = "abc" 进行加密,因为sha1对任何长度的二进制数据加密后都得到40位的16进制字符串。 ret = fp.hexdigest() print(ret) c64d3fcde20c5cd03142171e5ac47a87aa3c8ace # 生成一个40位长度的16进制的字符串。
第二步:去重工具:scrapy/dupefilters.py
集合(防止指纹(长度为40的字符串)) set(指纹1,指纹2,指纹3,。。。。,)
request_seen---看见函数,对指纹进行处理,
指纹在集合就直接返回,不进行下一步,return True
指纹不在集合,就加入集合,再进行下一步。
第三步:scrapy/core/scheduler.py/enqueue_request 调度器
enqueue_request 函数中:request对象的 dont_fliter=false且当前request对象的签名在集合中。那么就将当前的请求对象放入请求队列中去。
具体代码分析:https://blog.csdn.net/Mr__lqy/article/details/85859361
输出log日志
settings中 LOG_FILE = "0769.log" LOG_LEVEL = "DEBUG" 控制台不做任何输出,结果都写在名为0769的日志文件中。
scrapy抓取json数据
图片下载保存方法一:urllib.request
import urllib.request class DouyumeinvPipeline(object): def process_item(self, item, spider): url = item["imgurl"] name = item["id"] urllib.request.urlretrieve(url,"pic/{}.jpg".format(name)) # 直接使用urllib.request中的urlretrieve函数,直接将请求的资源保存指定路径 return item
图片下载保存方法二:Imagepipeline
# setting.py中设置: ITEM_PIPELINES = { 'douyuimage.pipelines.ImagePipeLine': 1, }
# 实际上图片是全部默认保存在full文件夹中,full文件夹则保存在IMAGE_STORE这个路径下。 IMAGES_STORE = r'C:\Users\Administrator\Desktop\img' IMAGES_EXPIRES = 90 # pipelines.py中设置: class ImagePipeLine(ImagesPipeline): def get_media_requests(self, item, info):
# 每个item都会经过这个函数,然后这个函数在发起一个请求对象,将结果传给 item_completed函数 url = item["img_url"] yield scrapy.Request(url) def item_completed(self, results, item, info): # results = [(True,{"path":"full/xxxxxx.jpg"})] image_path = [x['path'] for ok, x in results if ok] # 仅仅是图片路径是:full/xxxx.jpg if not image_path: raise DouyuimageItem("Item contains no images") item['image_path'] = image_path return item # 在items中设置: class DouyuimageItem(scrapy.Item): # define the fields for your item here like: # name = scrapy.Field() id = scrapy.Field() name = scrapy.Field() img_url = scrapy.Field() image_path = scrapy.Field() pass # spider.py中设置: class ImgdownSpider(scrapy.Spider): name = 'imgdown' allowed_domains = ['www.douyu.com'] current_page = 1 start_urls = ['https://www.douyu.com/gapi/rknc/directory/yzRec/{}'.format(current_page)] def parse(self, response): item = DouyuimageItem() data = json.loads(response.body.decode())["data"] info_dict_list = data["rl"] for info_dict in info_dict_list: item["id"] = info_dict["rid"] item["name"] = info_dict["nn"] item["img_url"] = info_dict["rs16"] yield item # 运行的结果 {'id': 6837192, 'image_path': ['full/e63847f2205b92e0a6c46c17d68e37f708948337.jpg'], 'img_url': 'https://rpic.douyucdn.cn/asrpic/190916/6837192_6066111_26880_2_1546.jpg', 'name': '十三金丶'}
scrapy翻页抓取数据
翻页抓取数据方式一:页码递增法
import requests from bs4 import BeautifulSoup url = "https://blog.csdn.net/itcastcpp/article/list/1?" html_content = requests.get(url).content soup = BeautifulSoup(html_content,"html5lib") # 将网页的字节数据解析为html5的类型 div_list = soup.find_all("div",class_="article-item-box csdn-tracking-statistics") # 按照class的属性进行寻找。 for div in div_list: title = (div.find_all("h4")[0]).find_all("a")[0] # 获取h4下的a标签的文本值 print(title.get_text()) # 结果: 原 兄弟连区块链 Go 学习大纲-取得大纲试看视频联系微信yinchengak48 原 尹成学院golang学习快速笔记(2)表达式 原 尹成学院golang学习快速笔记(1)类型 原 区块链交易所基础开发(1)通过接口查询区块链各个币种的提币情况-ada 原 Golang精编100题-搞定golang面试
scrapy中cookie的设置方法
方式一:setting中设置即可
第一步,打开setting中的DEFAULT_REQUEST_HEADER的注释 在里面添加Cookie:"xxxx"的键值对,从浏览器中复制即可 # Override the default request headers: DEFAULT_REQUEST_HEADERS = { 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', 'Accept-Language': 'en', "Cookie": "xxx"} 第二步,打来# COOKIES_ENABLED = False的注释,不授权使用cookie 第三步,开启爬虫
方式二:在爬虫类中重写start_requests方法
# 第一步,在spider.py中重写start_requests方法,增加cookie def start_requests(self): url = self.start_urls[0] cookies = { "uuid_tt_dd":"xx", "dc_session_id":"xx, "smidV2":"xx", "UN":"xx", "Hm_ct_6bcd52f51e9b3dce32bec4a3997715ac":"xx", "acw_tc":"xx", "Hm_ct_e5ef47b9f471504959267fd614d579cd":"xx", "__yadk_uid":"xx", "firstDie":"xx", "UserName":"xx", "UserInfo":"xx", "UserToken":"xx", "UserNick":"xx", "BT":"xx", "p_uid":"xx", "Hm_lvt_e5ef47b9f471504959267fd614d579cd":"xx", "Hm_lvt_6bcd52f51e9b3dce32bec4a3997715ac":"xx", "acw_sc__v3":"xx", "acw_sc__v2":"xx", "dc_tos":"xx", "Hm_lpvt_6bcd52f51e9b3dce32bec4a3997715ac":"xx"} yield scrapy.Request(url,cookies=cookies,callback=self.parse) 第二步:保持# Disable cookies (enabled by default)的注释状态,不要打开 # COOKIES_ENABLED = False
总结:COOKIES_ENABLED = False 默认是注释的,也就是框架默认是能够使用cookie,框架默认使用的cookie需要在Scrapy.Resuest(url,cookies={})中设置。
开启 :COOKIES_ENABLED = False,此时cookie的配置需要在setting.py中的DEFAULT_REQUEST_HEADERR = {}中进行配置。
方式三:DownloadMiddleware中进行设置
第一步:setting.py中打开DOWNLOADER_MIDDLEWARES 的注释,因为设置要在这个类中进行。 DOWNLOADER_MIDDLEWARES = { 'blog.middlewares.BlogDownloaderMiddleware': 543, } 第二步:middleware中找到对应的BlogDownloaderMiddleware,其中的函数 def process_request(self,request,spider): return None 改为: def process_request(self, request, spider): cookie_from_web = "浏览器中粘贴的Cookies:xx" cookies = {} for cookie in cookie_from_web.split(";"): cookies[cookie.split('=')[0]] = cookie.split('=')[1] print(cookies) request.cookies = cookies return None 第三步: 启动爬虫程序
总结:cookie的设置,都是围绕着在请求对象中加入cookie值,怎么加,在哪里加,原则就是在给解析函数返回response的那一次加就可以。
1:框架的第一次请求时,加入可以,需要重写start_request函数,请求对象参数中加入cookies={}
2. 在下载中间键,process_request函数中,给request对象加属性值,必须字典格式,request.cookies={}
3. setting.py中配置默认的请求头,在默认请求头中添加cookies。
---恢复内容结束---