1. 可以进行要求的发送;

2. 如果要求过程中产生了Cookie,则该Cookie会被自动存储/携带在该session工具中。
创建一个session工具:session = requests.Session( )

利用session工具进行仿照登录post要求的发送(Cookie会被存储在session中)session工具对个人主页对应的get要求进行发送(携带了Cookie)

jspoffset函数2021年最全的Python爬虫进修笔记下 RESTful API

#####基于前一节代码之上####session = requests.Session()#爬取当前用户的干系用户信息'''手动获取Cookie(不推举) headers = { ‘'Cookie':'xxxx' }'''detail_url = 'http://www.renren.com/976279344/profile'detail_page_test = session.get(url = detail_url,headers = headers).textwith open('bobo.html','w',encoding = 'utf-8' ) as fp: fp.write(detail_page_test)4. 代{过}{滤}理理论讲解代{过}{滤}理:破解封 IP 这种反爬机制。
什么是代{过}{滤}理?代{过}{滤}理做事器。
代{过}{滤}理的浸染:打破自身 IP 被访问的限定可以隐蔽自身真实的 IP,免受攻击干系网站:快代{过}{滤}理西祠代{过}{滤}理代{过}{滤}理 ip 的类型:http:只能运用到 http 协议对应的 url 中https:只能运用到 https 协议对应的 url 中代{过}{滤}理ip的匿名度:透明:做事器知道该次要求利用了代{过}{滤}理,也知道要求对应的真实 ip匿名:知道利用了代{过}{滤}理,不知道真实 ip高匿:不知道利用了代{过}{滤}理,也不知道真实 ip5. 代{过}{滤}理在爬虫中的运用

import requestsurl = 'http://www.baidu.com/s?wd=ip'headers = { 'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36' }page_text = requests.get(url = url, headers = headers, proxies = {"http": "http://124.205.155.153:9090"}).textwith open('ip.html', 'w', encoding = 'utf-8') as fp: fp.write(page_text)六、高性能异步爬虫1. 异步爬虫概述同步:不同程序单元为了完成某个任务,在实行过程中需靠某种通信办法以折衷同等,称这些程序单元是同步实行的。
例如购物系统中更新商品库存,须要用 “行锁” 作为通信旗子暗记,让不同的更新要求逼迫排队顺序实行,那更新库存的操作是同步的。
简言之,同步意味着有序。
异步:为完成某个任务,不同程序单元之间过程中无需通信折衷,也能完成任务的办法,不干系的程序单元之间可以是异步的。
例如,爬虫下载网页。
调度程序调用下载程序后,即可调度其他任务,而无需与该下载任务保持通信以折衷行为。
不同网页的下载、保存等操作都是无关的,也无需相互关照折衷。
这些异步操作的完成时候并不愿定。
简言之,异步意味着无序。
目的:在爬虫中利用异步实现高性能的数据爬取操作。

import requestsheaders = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36'}urls = [ 'https://downsc.chinaz.net/Files/DownLoad/jianli/202102/jianli14667.rar', 'https://downsc.chinaz.net/Files/DownLoad/jianli/202102/jianli14665.rar', 'https://downsc.chinaz.net/Files/DownLoad/jianli/202102/jianli14648.rar']def get_content(url): print('正在爬取:', url) # get方法是一个壅塞的方法 response = requests.get(url=url, headers=headers) if response.status_code == 200: return response.contentdef parse_content(content): print('相应数据的长度为:', len(content))for url in urls: content = get_content(url) parse_content(content)2. 多线程and多线程

异步爬虫的办法:

多线程,多进程:(不建议)好处:可以为干系壅塞的操作单独开启线程或者进程,壅塞操作就可以异步实行弊端:无法无限制的开启多线程或者多进程3. 线程池and进程池线程池、进程池:(适当利用)好处:可以降落系统对进程或者线程创建和销毁的一个频率,从而很好地降落系统地开销。
弊端:池中线程或进程地数量是有上限的。
4. 线程池的基本利用

import time#利用单线程串行办法实行def get_page(str): print('正不才载:',str) time.sleep(2) print('下载成功:',str)name_list = ['xiaozi','aa','bb','cc']start_time = time.time()for i in range(len(name_list)): get_page(name_list[i])end_time = time.time()print('%d second' % (end_time-start_time))

#导入线程池模块对应的类import timefrom multiprocessing.dummy import Pool#利用线程池办法实行start_time = time.time()def get_page(str): print('正不才载:', str) time.sleep(2) print('下载成功:', str)name_list = ['xiaozi','aa','bb','cc']#实例化一个线程池工具pool = Pool(4) #线程池开辟4个线程#将列表中每一个列表元素通报给get_page进行处理pool.map(get_page, name_list)end_time = time.time()print(end_time - start_time)5. 线程池案例运用

# 需求:爬取梨视频视频数据import requestsimport osfrom multiprocessing.dummy import Poolfrom lxml import etreeimport randomheaders = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36'}# 原则:线程池处理的是壅塞且耗时的操作if __name__ == '__main__': # 天生一个存放视频的文件夹 if not os.path.exists('./video'): os.mkdir('./video') # 对下述url发起要求解析出视频详情页的url和视频的名称 url = 'https://www.pearvideo.com/category_5' page_text = requests.get(url=url, headers=headers).text tree = etree.HTML(page_text) li_list = tree.xpath('//ul[@id="listvideoListUl"]/li')urls = [] # 存储所有视频的链接和笔墨for li in li_list: detail_url = 'https://www.pearvideo.com/' + li.xpath('./div/a/@href')[0] name = li.xpath('./div/a/div[2]/text()')[0] + '.mp4' # print(detail_url,name) # 对详情页的url发起要求 detail_page_text = requests.get(url=detail_url, headers=headers).text # 从详情页中解析出视频的地址 #### 视频的方法在2021/02/27 不可利用,梨视频又变动了页面源码,mp4是动态加载出来的,mp4文件经ajax要求得到,须要抓包ajax #### 参考 https://www.cnblogs.com/qianhu/p/14027192.html的操作 detail_tree = etree.HTML(detail_page_text) name = detail_tree.xpath('//[@id="detailsbd"]/div[1]/div[2]/div/div[1]/h1/text()')[0] str_ = str(li.xpath('./div/a/@href')[0]).split('_')[1] ajax_url = 'https://www.pearvideo.com/videoStatus.jsp?' params = { 'contId': str_, 'mrd': str(random.random()) } ajax_headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36', 'Referer': 'https://www.pearvideo.com/video_' + str_ } dic_obj = requests.get(url=ajax_url, params=params, headers=ajax_headers).json() video_url = dic_obj["videoInfo"]['videos']["srcUrl"] video_true_url = '' s_list = str(video_url).split('/') for i in range(0, len(s_list)): if i < len(s_list) - 1: video_true_url += s_list[i] + '/' else: ss_list = s_list[i].split('-') for j in range(0, len(ss_list)): if j == 0: video_true_url += 'cont-' + str_ + '-' elif j == len(ss_list) - 1: video_true_url += ss_list[j] else: video_true_url += ss_list[j] + '-' dic = { 'name': name, 'url': video_true_url } urls.append(dic)def get_video_data(dic): urll = dic['url'] data = requests.get(url=urll, headers=headers).content path = './video/' + dic['name'] + '.mp4' print(dic['name'], '正不才载.......') # 持久化存储操作 with open(path, 'wb') as fp: fp.write(data) print(dic['name']+ '.mp4', '下载成功!
')# 利用线程池对视频数据进行要求(较为耗时的壅塞操作)pool = Pool(4)pool.map(get_video_data, urls)pool.close()pool.join()
6. 协程干系观点回顾协程:英文叫做 Coroutine,又称微线程,纤程,协程是一种用户态的轻量级线程。
协程拥有自己的寄存器高下文和栈。
协程调度切换时,将寄存器高下文和栈保存到其他地方,在切回来的时候,规复先前保存的寄存器高下文和栈。
因此协程能保留上一次调用时的状态,即所有局部状态的一个特定组合,每次过程重入时,就相称于进入上一次调用的状态。
协程实质上是个单进程,协程相对付多进程来说,无需线程高下文切换的开销,无需原子操作锁定及同步的开销,编程模型也非常大略。
我们可以利用协程来实现异步操作,比如在网络爬虫场景下,我们发出一个要求之后,须要等待一定的韶光才能得到相应,但其实在这个等待过程中,程序可以干许多其他的事情,等到相应得到之后才切换回来连续处理,这样可以充分利用 CPU 和其他资源,这便是异步协程的上风。
单线程+异步协程:(推举)event_loop:事宜循环,相称于一个无限循环,我们可以把一些函数注册到这个事宜循环上,当知足某些条件的时候,函数就会被循环实行。
coroutine:协程工具,我们可以将协程工具注册到事宜循环中,它会被事宜循环调用,我们可以利用 async 关键字来定义一个方法,这个方法在调用时不会立即实行,而是返回一个协程工具。
task:任务,他是对协程工具的进一步封装,包含了任务的各个状态。
future:代表将来实行或还没有实行的任务,实际上和 task 没有实质差异。
async:定义一个协程。
await:用来挂起壅塞方法的实行。
7. 协程干系操作回顾

import asyncioasync def request(url): print('正在要求的url是',url) print('要求成功,',url) return url#asyncio润色的函数,调用之后返回的一个协程工具c = request('www.baidu.com')# #创建一个事宜循环工具# loop = asyncio.get_event_loop()## #将协程工具注册到loop中,然后启动loop# loop.run_until_complete(c)# #task的利用# loop = asyncio.get_event_loop()# #基于loop创建一个task任务工具# task = loop.create_task(c)# print(task)## loop.run_until_complete(task)# print(task)# #future的利用# loop = asyncio.get_event_loop()# task = asyncio.ensure_future(c)# loop.run_until_complete(task)# print(task)def callback_func(task): #result返回的便是任务工具中封装的协程工具对应函数的返回值 print(task.result())#绑定回调loop = asyncio.get_event_loop()task = asyncio.ensure_future(c)#将回调函数绑定到任务工具中task.add_done_callback(callback_func)loop.run_until_complete(task)8. 多任务异步协程实现

import timeimport asyncioasync def request(url): print('正不才载',url) #在异步协程中如果涌现了同步模块干系的代码,那么就无法实现异步 #time.sleep(2) #当asyncio中碰着壅塞操作,必须手动挂起 await asyncio.sleep(2) print('下载完毕',url)start = time.time()urls =[ 'www.baidu.com', 'www.sougou.com', 'www.goubanjia.com']#任务列表:存放多个任务工具stasks = []for url in urls: c = request(url) task = asyncio.ensure_future(c) stasks.append(task)loop = asyncio.get_event_loop()#须要将任务列表封装到wait中loop.run_until_complete(asyncio.wait(stasks))print(time.time()-start)9. aiohttp 模块引出

######未能实现异步进程,还是同步操作import requestsimport asyncioimport timestart = time.time()urls = [ 'http://127.0.0.1:1080/bobo', 'http://127.0.0.1:1080/jay', 'http://127.0.0.1:1080/tom']async def get_page(url): print('正不才载', url) #requests模块发起的要求是基于同步的,不能在异步模块中利用,否则会中断异步操作,必须利用基于异步的网络要求模块进行url的要求发送 #aiphttp模块引入 response = requests.get(url = url) print('下载完毕', response.text)tasks = []for url in urls: c = get_page(url) task = asyncio.ensure_future(c) tasks.append(task)loop = asyncio.get_event_loop()loop.run_until_complete(asyncio.wait(tasks))end = time.time()print('总耗时:', end-start)10. aiohttp + 多任务异步协程实现异步爬虫

#环境的安装 pip install aiohttp#利用aiohttp模块中的ClientSessionimport asyncioimport timeimport aiohttpstart = time.time()urls = [ 'http://www.baidu.com', 'http://www.sougou.com', 'http://www.taobao.com']async def get_page(url): async with aiohttp.ClientSession() as session: #get()、post(): #headers,params/data,proxy='http://ip:port' async with await session.get(url) as response: #text()返回的是字符串形式的相应数据 #read()返回的是二进制形式的相应数据 #json()返回的是json工具 #把稳:在获取相应数据操作之前,一定要利用await手动挂起 page_text = await response.text() #print(page_text)tasks = []for url in urls: c = get_page(url) task = asyncio.ensure_future(c) tasks.append(task)loop = asyncio.get_event_loop()loop.run_until_complete(asyncio.wait(tasks))end = time.time()print('总耗时:', end-start)七、动态加载数据处理1. selenium简介问题:selenium模块和爬虫之间具有若何的关联?便捷地获取网站中动态加载的数据便捷实现仿照登录什么是selenium模块?基于浏览器自动化的一个模块。
2. selenium初试

selenium利用流程:

环境安装:pip install selenium下载一个对应浏览器的驱动程序(以谷歌浏览器为例)实例化一个浏览器工具编写基于浏览器自动化的操作代码发起要求:get(url)标签定位:find系列方法标签交互:send_keys('xxxxxx')实行js程序:excute_script('jsCode')提高、退却撤退:forward( )、back( )关闭浏览器:quit( )

# selenium操纵浏览器#### Tip:作者Chrome是88版本,直接下载88的chromedriver成功运行from selenium import webdriverfrom lxml import etreefrom time import sleep# 实例化一个浏览器工具(传入浏览器的驱动程序)bro = webdriver.Chrome(executable_path='./chromedriver.exe')# 让浏览器发起一个指定的url对应要求bro.get('http://scxk.nmpa.gov.cn:81/xk/') # 获取浏览器当前页面的页面源码数据page_text = bro.page_source# 解析企业名称tree = etree.HTML(page_text)li_list = tree.xpath('//ul[@id="gzlist"]/li')for li in li_list: name = li.xpath('./dl/@title')[0] print(name)sleep(5)bro.quit()3. selenium其他自动化操作

from selenium import webdriverfrom time import sleepbro = webdriver.Chrome(executable_path='./chromedriver.exe')bro.get('https://www.taobao.com/')# 标签定位search_input = bro.find_element_by_id('q')# 标签的交互search_input.send_keys('iphone')# 实行一组js程序 相称于F12--Console实行js代码bro.execute_script('window.scrollTo(0,document.body.scrollHeight)')sleep(2)# 点击搜索按钮btn = bro.find_element_by_css_selector('.btn-search')btn.click()bro.get('https://baidu.com/')sleep(2)# 回退bro.back()sleep(2)# 提高bro.forward()sleep(5)bro.quit()4. iframe 处理+动作链

selenium处理iframe:

如果定位的标签存在于iframe标签之中,则必须利用switch_to.frame(id)动作链(拖动):from selenium.webdriver import ActionChains实例化一个动作链工具:action = ActionChains(bro)click_and_hold(div):长按且点击move_by_offset(x,y)perform( ):让动作链立即实行action.release( ):开释动作链工具

from selenium import webdriverfrom time import sleep# 导入动作链对应的类from selenium.webdriver import ActionChainsbro = webdriver.Chrome(executable_path='./chromedriver.exe')bro.get('https://www.runoob.com/try/try.php?filename=jqueryui-example-droppable')# 如果定位的标签是存在与iframe标签之中的,直接通过find办法会报错,则必须通过其余的操作来进行标签定位bro.switch_to.frame('iframeResult') #切换浏览器标签定位的浸染域div = bro.find_element_by_id('draggable')# 动作链action = ActionChains(bro) #实例化动作链工具# 点击并且长按指定的标签action.click_and_hold(div)for i in range(5): #perform 表示立即实行动作链操作 #move_by_offset(x,y) x表示水平方向,y表示竖直方向 action.move_by_offset(11, 0).perform() sleep(0.3)# 开释动作链action.release()bro.quit()5. selenium仿照登录空间

#仿照登录QQ空间,运行前须要将代码中“QQ号码”和“QQ密码”改写from selenium import webdriverfrom time import sleepbro = webdriver.Chrome(executable_path='./chromedriver.exe')bro.get('https://qzone.qq.com/')bro.switch_to.frame('login_frame')a_tag = bro.find_element_by_id('switcher_plogin')a_tag.click()userName_tag = bro.find_element_by_id('u')password_tag = bro.find_element_by_id('p')sleep(1)userName_tag.send_keys('QQ号码')password_tag.send_keys('QQ密码')sleep(1)btn = bro.find_element_by_id('login_button')btn.click()sleep(3)bro.quit()6. 无头浏览器+规避操作

from selenium import webdriverfrom time import sleep#实现无可视化界面from selenium.webdriver.chrome.options import Options#实现规避检测from selenium.webdriver import ChromeOptions#实现无可视化界面的操作chrome_options = Options()chrome_options.add_argument('--headless')chrome_options.add_argument('--disable-gpu')#实现规避检测option = ChromeOptions()option.add_experimental_option('excludeSwitches', ['enable-automation'])#如何实现让selenium规避被检测到的风险bro = webdriver.Chrome(executable_path='./chromedriver.exe', chrome_options=chrome_options,options=option)#无可视化界面(无头浏览器) phantomJsbro.get('https://www.baidu.com')print(bro.page_source)sleep(2)bro.quit()7. 超级鹰的基本利用

超级鹰:

注册:普通用户登录:普通用户题分查询:充值软件ID——创建一个软件ID下载示例代码8. 12306仿照登录

编码流程:

利用selenium打开登录界面对当前selenium打开的这张界面进行截图对截取的图片进行局部区域(验证码图片)的裁剪好处:将验证码图片和仿照登录进行逐一对应利用超级鹰识别验证码图片(坐标)

#!/usr/bin/env python# coding:utf-8import requestsfrom hashlib import md5########下述为超级鹰示例代码class Chaojiying_Client(object): def __init__(self, username, password, soft_id): self.username = username password = password.encode('utf8') self.password = md5(password).hexdigest() self.soft_id = soft_id self.base_params = { 'user': self.username, 'pass2': self.password, 'softid': self.soft_id, } self.headers = { 'Connection': 'Keep-Alive', 'User-Agent': 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0)', } def PostPic(self, im, codetype): """ im: 图片字节 codetype: 题目类型 参考 http://www.chaojiying.com/price.html """ params = { 'codetype': codetype, } params.update(self.base_params) files = {'userfile': ('ccc.jpg', im)} r = requests.post('http://upload.chaojiying.net/Upload/Processing.php', data=params, files=files, headers=self.headers) return r.json() def ReportError(self, im_id): """ im_id:报错题目的图片ID """ params = { 'id': im_id, } params.update(self.base_params) r = requests.post('http://upload.chaojiying.net/Upload/ReportError.php', data=params, headers=self.headers) return r.json()############上述为超级鹰的示例代码# 利用selenium打开登录页面from selenium import webdriverimport timefrom PIL import Imagefrom selenium.webdriver import ActionChainsbro = webdriver.Chrome(executable_path='./chromedriver.exe')bro.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument", { "source": """ Object.defineProperty(navigator, 'webdriver', { get: () => undefined }) """})# bro.execute_script(script)bro.get('https://kyfw.12306.cn/otn/resources/login.html')#最大化浏览器窗口bro.maximize_window()time.sleep(1)# 先点击选择 账号登录zhanghao_tag = bro.find_element_by_class_name('login-hd-account')zhanghao_tag.click()time.sleep(1)# save_screenshot便是将当前页面进行截图且保存bro.save_screenshot('aa.png')#确定验证码图片对应的左上角和右下角的坐标(裁剪的区域就确定)code_img_ele = bro.find_element_by_class_name('touclick-wrapper')location = code_img_ele.location # 验证码图片左上角的坐标 x,yprint('location:', location)size = code_img_ele.size #验证码标签对应的长和宽print('size:', size)# 左上角和右下角坐标 #此处 1.25 缘故原由是作者window电脑默认显示布局为125%(电脑设置--显示--缩放与布局),不乘1.25取不到图片精确位置rangle = (location['x']1.25, location['y']1.25, (location['x']+size['width'])1.25, (location['y']+size['height'])1.25)# 至此验证码图片区域就确定下来了i = Image.open('./aa.png')code_img_name = './code.png'# crop根据指定区域进行图片裁剪frame = i.crop(rangle)frame.save(code_img_name)time.sleep(3)# 将验证码图片提交给超级鹰进行识别chaojiying = Chaojiying_Client('超级账号', '超级密码', '软件ID')im = open('code.png', 'rb').read()print(chaojiying.PostPic(im, 9004)['pic_str'])result = chaojiying.PostPic(im, 9004)['pic_str']all_list = [] #要存储即将被点击的点的坐标 [[x1,y1],[x2,y2]]if '|' in result: list_1 = result.split('|') count_1 = len(list_1) for i in range(count_1): xy_list = [] x = int(list_1[i].split(',')[0]) y = int(list_1[i].split(',')[1]) xy_list.append(x) xy_list.append(y) all_list.append(xy_list)else: x = int(result.split(',')[0]) y = int(result.split(',')[1]) xy_list = [] xy_list.append(x) xy_list.append(y) all_list.append(xy_list)print(all_list)# 遍历列表,利用动作链对每一个列表元素对应的x,y指定的位置进行点击操作for l in all_list: x = l[0] y = l[1] #这里的/1.25,是由于,电脑设置125%,而网页是100%的,以是,要确定网页中对应位置,除以1.25即可 ActionChains(bro).move_to_element_with_offset(code_img_ele, x/1.25, y/1.25).click().perform() time.sleep(1)bro.find_element_by_id('J-userName').send_keys('12306账号')time.sleep(1)bro.find_element_by_id('J-password').send_keys('12306密码')time.sleep(1)bro.find_element_by_id('J-login').click()time.sleep(5)# # 滑块操作,12306检测selenium,,,,滑块总是刷新重试,# action = ActionChains(bro)# try:# slider = bro.find_element_by_css_selector('#nc_1_n1z')# action.click_and_hold(slider)# action.move_by_offset(300, 0).perform()# time.sleep(15)# action.release()# except Exception as e:# print(e)bro.quit()八、scrapy框架1. scrapy框架初识什么是框架?便是一个集成了很多功能并且具有很强通用性的一个项目模板。
如何学习框架?专门学习框架封装的各种功能的详细用法。
什么是scrapy?爬虫中封装好的一个明星框架。
功能:高性能的持久化存储,异步的数据下载,高性能的数据解析,分布式2. scrapy基本利用

scrapy框架的基本利用:

环境的安装:mac or linux:pip install scrapywindows:pip install wheel下载twisted安装twisted:pip install Twisted-20.3.0-cp39-cp39-win_amd64.whlpip install pywin32pip install scrapy测试:在终端里录入scrapy指令,没有报错即表示安装成功!
创建一个工程:scrapy startproject xxxProcd xxxPro在spiders子目录中创建一个爬虫文件scrapy genspider spiderName www.xxx.com实行工程:scrapy crawl spiderName

###firstBlood__firstimport scrapyclass FirstSpider(scrapy.Spider): #爬虫文件的名称:便是爬虫源文件的一个唯一标识 name = 'first' #许可的域名:用来限定start_urls列表中哪些url可以进行要求发送 # allowed_domains = ['www.baidu.com'] #起始的url列表:该列表中存放的url会被scrapy自动进行要求的发送 start_urls = ['https://www.baidu.com/', 'https://www.sogou.com/'] #用作于数据解析:response参数表示的便是要求成功后对应的相应工具 def parse(self, response): print(response)3. scrapy数据解析操作

import scrapyclass QiubaiSpider(scrapy.Spider): name = 'qiubai' #allowed_domains = ['www.xxx.com'] start_urls = ['https://www.qiushibaike.com/text/'] def parse(self, response): #解析作者的名称+段子的内容 div_list = response.xpath('//div[@id="col1 old-style-col1"]/div') for div in div_list: #xpath返回的是列表,当时列表元素一定是Selector类型的工具 #extract可以将Selector工具中data参数存储的字符串提取出来 author = div.xpath('./div[1]/a[2]/h2/text()')[0].extract() #列表调用了extract之后。
则表示将列表中每一个Selector工具中data对应的字符串提取了出来 content = div.xpath('./a[1]/div/span//text()').extract() content = ''.join(content) print(author,content) break
4. 基于终端指令的持久化存储

scrapy持久化存储:

基于终端指令:哀求:只可以将parse方法的返回值存储到本地的文本文件中把稳:持久化存储对应的文本文件类型只可以为:json、jsonlines、jl、csv、xml、marshal、pickle指令:scrapy crawl xxx -o filePath好处:简洁高效便捷缺陷:局限性比较强(数据只可以存储到指定后缀的文本文件中)5. 基于管道持久化存储操作

基于管道:

编码流程:数据解析在item类中定义干系的属性将解析的数据封装到item类型的工具将item类型的工具提交给管道进行持久化存储的操作在管道类的process_item中要将其吸收到的item工具中存储的数据进行持久化存储操作在配置文件中开启管道好处:通用性强。

口试题:将爬取到的数据一份存储到本地,一份存储到数据库,如何实现?

管道文件中一个管道类对应的是将数据存储到一种平台爬虫文件提交的item只会给管道文件中第一个被实行的管道类吸收process_item中的return item表示将item通报给下一个即将被实行的管道类6. 全站数据爬取

基于spider的全站数据爬取:便是将网站中某板块下的全部页码对应的页面数据进行爬取。

爬取:校花网明星写真的名称实现办法:将所有页面的url添加到start_urls列表(不推举)自行手动进行要求发送(推举)

'''------------校花网xiaohua.py----------------'''# -- coding: utf-8 --import scrapyclass XiaohuaSpider(scrapy.Spider): name = 'xiaohua' # allowed_domains = ['www.xxx.com'] start_urls = ['http://www.521609.com/tuku/mxxz/'] #天生一个通用的url模板(不可变) url = 'http://www.521609.com/tuku/mxxz/index_%d.html' page_num = 2 def parse(self, response): li_list = response.xpath('/html/body/div[4]/div[3]/ul/li') for li in li_list: img_name = li.xpath('./a/p/text()').extract_first() print(img_name) if self.page_num <= 28: new_url = format(self.url%self.page_num) self.page_num += 1 #手动要求发送:callback回调函数是专门用作于数据解析 yield scrapy.Request(url=new_url,callback=self.parse)'''---------------校花网pipelines.py--------------------'''# -- coding: utf-8 --# Define your item pipelines here## Don't forget to add your pipeline to the ITEM_PIPELINES setting# See: https://doc.scrapy.org/en/latest/topics/item-pipeline.htmlclass XiaohuaproPipeline(object): def process_item(self, item, spider): return item'''----------------校花网settings.py部分代码---------------------------'''ROBOTSTXT_OBEY = FalseLOG_LEVEL = 'ERROR'USER_AGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36'7. 五大核心组件

五大核心组件:

Spiders:产生URL,对URL进行手动发送进行数据解析引擎(Scrapy Engine):数据流处理触发事务调度器(Scheduler):过滤器去重去重后的要求工具压到行列步队中下载器(Downloader):卖力获取页面数据并供应给引擎,而后供应给Spider项目管道(Item Pipeline):卖力处理爬虫从网页中抽取的实体,页面被爬虫解析所需的数据存入item后,将被发送到管道,经由特定的次序处理数据,末了存入本地文件或者数据库。
8. 要求传参利用场景:如果爬取解析的数据不在同一张页面中。
(深度爬取)需求:爬取boss的岗位名称和岗位描述

#### 我考试测验着并未有啥结果.......等大佬import scrapyfrom bossPro.items import BossproItemclass BossSpider(scrapy.Spider): name = 'boss' # allowed_domains = ['www.xxx.com'] start_urls = ['https://www.zhipin.com/c100010000/?page=1&ka=page-1'] url = 'https://www.zhipin.com/c100010000/?page=%d' page_num = 2 #回调函数吸收item def parse_detail(self,response): item = response.meta['item'] job_desc = response.xpath('//[@id="main"]/div[3]/div/div[2]/div[2]/div[1]/div//text()').extract() job_desc = ''.join(job_desc) print(job_desc) item['job_desc'] = job_desc yield item #解析首页中的岗位名称 def parse(self, response): li_list = response.xpath('//[@id="main"]/div/div[2]/ul/li') for li in li_list: item = BossproItem() job_name = li.xpath('.//div/div[1]/div[1]/div/div[1]/span[1]/a/text()').extract_first() item['job_name'] = job_name print(job_name) detail_url = 'https://www.zhipin.com' + li.xpath('.//div/div[1]/div[1]/div/div[1]/span[1]/a/@href').extract_first() #对详情页发要求获取详情页的页面源码数据 #手动要求的发送 #要求传参:meta={},可以将meta字典通报给要求对应的回调函数 yield scrapy.Request(detail_url,callback=self.parse_detail,meta={'item':item}) #分页操作 if self.page_num <= 5: new_url = format(self.url%self.page_num) self.page_num += 1 yield scrapy.Request(new_url,callback=self.parse)9. scrapy图片爬取

图片数据爬取之ImagesPipline:

基于scrapy爬取字符串类型的数据和爬取图片类型的数据差异?字符串:只须要基于xpath进行解析且提交管道进行持久化存储图片:xpath解析出图片的src属性值,单独的对图片地址发起要求获取二进制类型的数据ImagesPipeline:只须要将img的src的属性值进行解析,提交到管道,管道就会对图片的src进行要求发送获取图片的二进制类型的数据,且还会帮我们进行持久化存储。
需求:爬取站长素材的高清图片利用流程:数据解析(图片的地址)将存储图片地址的item提交到指定的管道类在管道文件中自己定制一个基于ImagesPipeLine的一个管道类get_media_request( )file_pathitem_completed在配置文件中操作指定图片存储目录:IMAGES_STORE = './imgs_ZYZhang'指定开启的管道:自定制的管道类

'''----------------爬取站长素材高清图片 img.py-----------------------'''# -- coding: utf-8 --import scrapyfrom imgsPro.items import ImgsproItemclass ImgSpider(scrapy.Spider): name = 'img' # allowed_domains = ['www.xxx.com'] start_urls = ['http://sc.chinaz.com/tupian/'] def parse(self, response): div_list = response.xpath('//div[@id="container"]/div') for div in div_list: #把稳:利用伪属性 src2 src = 'https:' + div.xpath('./div/a/img/@src2').extract_first() item = ImgsproItem() item['src'] = src yield item'''----------------------爬取站长素材高清图片 pipelines.py---------------------------''' # -- coding: utf-8 --# Define your item pipelines here## Don't forget to add your pipeline to the ITEM_PIPELINES setting# See: https://doc.scrapy.org/en/latest/topics/item-pipeline.html# class ImgsproPipeline(object):# def process_item(self, item, spider):# return itemfrom scrapy.pipelines.images import ImagesPipelineimport scrapyclass imgsPileLine(ImagesPipeline): #可以根据图片地址进行图片数据的要求 def get_media_requests(self, item, info): yield scrapy.Request(item['src']) #指定图片存储的路径 def file_path(self, request, response=None, info=None): imgName = request.url.split('/')[-1] return imgName def item_completed(self, results, item, info): return item #返回给下一个即将被实行的管道类'''---------------------------------爬取站长素材高清图片 items.py-----------------------------'''# -- coding: utf-8 --# Define here the models for your scraped items## See documentation in:# https://doc.scrapy.org/en/latest/topics/items.htmlimport scrapyclass ImgsproItem(scrapy.Item): # define the fields for your item here like: src = scrapy.Field() # pass'''------------------------------爬取站长素材高清图片 setting.py部分代码-------------------'''#指定图片存储的目录IMAGES_STORE = './imgs_ZYZhang'ITEM_PIPELINES = { 'imgsPro.pipelines.imgsPileLine': 300,}LOG_LEVEL = 'ERROR'# Crawl responsibly by identifying yourself (and your website) on the user-agentUSER_AGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36'# Obey robots.txt rulesROBOTSTXT_OBEY = False10. 中间件下载中间件:位置:引擎和下载器之间浸染:批量拦截到全体工程中所有的要乞降相应拦截要求:UA伪装:process_request代{过}{滤}理IP:process_exception:return request拦截相应:修改相应数据,相应工具网易新闻爬取11. 网易新闻

需求:爬取网易新闻的新闻数据(标题和内容)

通过网易新闻的首页解析出几大板块对应的详情页的url(履历证,无动态加载)每个板块点击后,个中的新闻标题都是动态加载出来的(动态加载)通过解析出每一条新闻详情页的url,获取详情页的页面源码,解析出新闻内容

'''-------------------------------网易新闻 wangyi.py------------------------'''# -- coding: utf-8 --import scrapyfrom selenium import webdriverfrom wangyiPro.items import WangyiproItemclass WangyiSpider(scrapy.Spider): name = 'wangyi' # allowed_domains = ['www.cccom'] start_urls = ['https://news.163.com/'] models_urls = [] #存储五个板块对应详情页的url #解析五大板块对应详情页的url #实例化一个浏览器工具 def __init__(self): self.bro = webdriver.Chrome(executable_path='F:\PythonProjects\爬虫\动态加载数据处理\chromedriver.exe') def parse(self, response): li_list = response.xpath('//[@id="index2016_wrap"]/div[1]/div[2]/div[2]/div[2]/div[2]/div/ul/li') alist = [3,4,6,7,8] for index in alist: model_url = li_list[index].xpath('./a/@href').extract_first() self.models_urls.append(model_url) #依次对每一个板块对应的页面进行要求 for url in self.models_urls: #对每一个板块的url进行要求发送 yield scrapy.Request(url,callback=self.parse_model) #每一个板块对应的新闻标题干系的内容都是动态加载 def parse_model(self,response): #解析每一个板块页面中对应新闻的标题和新闻详情页的url # response.xpath() div_list = response.xpath('/html/body/div/div[3]/div[4]/div[1]/div/div/ul/li/div/div') for div in div_list: title = div.xpath('./div/div[1]/h3/a/text()').extract_first() new_detail_url = div.xpath('./div/div[1]/h3/a/@href').extract_first() item = WangyiproItem() item['title'] = title #对新闻详情页的url发起要求 yield scrapy.Request(url=new_detail_url, callback=self.parse_detail, meta={'item': item}) def parse_detail(self,response): # 解析新闻内容 content = response.xpath('//[@id="content"]/div[2]//text()').extract() content = ''.join(content) item = response.meta['item'] item['content'] = content yield item def closed(self, spider): self.bro.quit()'''-------------------------------网易新闻 pipelines.py-----------------------------------'''# -- coding: utf-8 --# Define your item pipelines here## Don't forget to add your pipeline to the ITEM_PIPELINES setting# See: https://doc.scrapy.org/en/latest/topics/item-pipeline.htmlclass WangyiproPipeline(object): def process_item(self, item, spider): print(item) return item'''-------------------------------网易新闻 middlewares.py-------------------------'''# -- coding: utf-8 --# Define here the models for your spider middleware## See documentation in:# https://doc.scrapy.org/en/latest/topics/spider-middleware.htmlfrom scrapy import signalsfrom scrapy.http import HtmlResponsefrom time import sleepclass WangyiproDownloaderMiddleware(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. 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 return None # 通过该方法拦截五大板块对应的相应工具,进行修改,使其知足需求 def process_response(self, request, response, spider): #spider爬虫工具 bro = spider.bro #获取了在爬行动物中定义的浏览器工具 #挑选出指定的相应工具进行修改 # 通过url指定request # 通过request指定response if request.url in spider.models_urls: bro.get(request.url) #五个板块对应的url进行要求 sleep(3) page_text = bro.page_source #包含了动态加载的新闻数据 #response #五大板块对应的相应工具 #针对定位到的这些response进行修改 #实例化一个新的相应工具(符合需求:包含动态加载出的新闻数据),替代原来旧的相应工具 #如何获取动态加载出的新闻数据? #基于selenium便捷的获取动态加载数据 new_response = HtmlResponse(url=request.url, body=page_text, encoding='utf-8', request=request) return new_response else: #response #其他要求对应的相应工具 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'''-----------------------------网易新闻 setting.py部分代码---------------------------------'''#USER_AGENT = 'wangyiPro (+http://www.yourdomain.com)'USER_AGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36'# Obey robots.txt rulesROBOTSTXT_OBEY = False# Enable or disable downloader middlewares# See https://doc.scrapy.org/en/latest/topics/downloader-middleware.htmlDOWNLOADER_MIDDLEWARES = { 'wangyiPro.middlewares.WangyiproDownloaderMiddleware': 543,}ITEM_PIPELINES = { 'wangyiPro.pipelines.WangyiproPipeline': 300,}LOG_LEVEL = 'ERROR'12. CrawlSpider的全站数据爬取

CrawlSpider:基于Spider的一个子类

全站数据爬取的办法基于Spider:手动要求发送基于CrawlSpiderCrawlSpider的利用:创建一个工程cd XXX创建爬虫文件(CrawlSpider)scrapy genspider -t crawl xxx www.xxxx.com链接提取器(LinkExtractor):根据指定规则(allow="正则")进行指定链接的提取规则解析器(Rule):将链接提取器提取到的链接进行指定规则(callback)的解析操作需求:爬取阳光热线网站中的编号,新闻标题,新闻内容,标号剖析:爬取的数据没有在同一张页面中可以利用链接提取器提取所有的页码链接让链接提取器提取所有的问政详情页链接

'''---------------------阳光问政 sun.py---------------------------''''''网站页面源码跟视频课有改动,建议follow先改False爬一下,不然随意马虎被封IP,有兴趣的可以改改,搞个代{过}{滤}理啥的再爬'''# -- coding: utf-8 --import scrapyfrom scrapy.linkextractors import LinkExtractorfrom scrapy.spiders import CrawlSpider, Rulefrom sunPro.items import SunproItem, DetailItem# 需求:爬取阳光热线网站中的编号,新闻标题,新闻内容,标号class SunSpider(CrawlSpider): name = 'sun' # allowed_domains = ['www.xxx.com'] start_urls = ['http://wz.sun0769.com/political/index/politicsNewest?id=1&page='] #链接提取器:根据指定规则(allow="正则")进行指定链接的提取 link = LinkExtractor(allow=r'id=1&page=\d+') link_detail = LinkExtractor(allow=r'index\?id=\d+') rules = ( #规则解析器:将链接提取器提取到的链接进行指定规则(callback)的解析操作 Rule(link, callback='parse_item', follow=False), #follow=True:可以将链接提取器 连续浸染到 链接提取器提取到的链接 所对应的页面中 Rule(link_detail, callback='parse_detail') ) #http://wz.sun0769.com/political/politics/index?id=490505 #http://wz.sun0769.com/political/politics/index?id=490504 # 解析新闻编号和新闻的标题 # 如下两个解析方法中是不可以实现要求传参!
# 无法将两个解析方法解析的数据存储到同一个item中,可以依次存储到两个item中 def parse_item(self, response): #把稳:xpath表达式中不可以涌现tbody标签 li_list = response.xpath('/html//div[2]/div[3]/ul[2]/li') for li in li_list: new_num = li.xpath('./span[1]/text()').extract_first() new_title = li.xpath('./span[3]/a/text()').extract_first() item = SunproItem() item['title'] = new_title item['new_num'] = new_num yield item #解析新闻内容和新闻编号 def parse_detail(self,response): new_id = response.xpath('/html//div[3]/div[2]/div[2]/div[1]/span[4]/text()').extract_first().strip().replace("\r\n", "").replace(" ", "") new_content = response.xpath('/html//div[3]/div[2]/div[2]/div[2]/pre/text()').extract() new_content = ''.join(new_content) # print(new_id,new_content) item = DetailItem() item['content'] = new_content item['new_id'] = new_id yield item'''-------------------------------pipelines.py------------------------------'''# -- coding: utf-8 --# Define your item pipelines here## Don't forget to add your pipeline to the ITEM_PIPELINES setting# See: https://doc.scrapy.org/en/latest/topics/item-pipeline.htmlclass SunproPipeline(object): def process_item(self, item, spider): #如何剖断item的类型 #将数据写入数据库时,如何担保数据的同等性 if item.__class__.__name__ == 'DetailItem': print(item['new_id'],item['content']) else: print(item['new_num'],item['title']) return item'''---------------------------items.py----------------------'''# -- coding: utf-8 --# Define here the models for your scraped items## See documentation in:# https://doc.scrapy.org/en/latest/topics/items.htmlimport scrapyclass SunproItem(scrapy.Item): # define the fields for your item here like: title = scrapy.Field() new_num = scrapy.Field()class DetailItem(scrapy.Item): new_id = scrapy.Field() content = scrapy.Field()
13. 分布式概述及搭建

分布式爬虫:

观点:我们须要搭建一个分布式的机群,让其对一组资源进行分布联合爬取。
浸染:提升爬取数据的效率

如何实现分布式?

安装一个scrapy-redis的组件原生的scrapy是不可以实现分布式爬虫的,必须要让scrapy-redis组件一起实现分布式爬虫。

为什么原生的scrapy不可以实现分布式?

调度器不可以被分布式机群共享管道不可以被分布式机群共享

scrapy-redis组件浸染:

可以给原生的scrapy框架供应可以被共享的管道调度器

scrapy-redis实现流程:

创建一个工程创建一个基于CrawlSpider的爬虫文件修合法前的爬虫文件:导包:from scrapy_redis.spiders import RedisCrawlSpider将start_urls和allowed_domains进行注释添加一个新属性:redis_key = ' ' 可以被共享的调度器行列步队的名称编写数据解析干系的操作将当前爬行动物的父类修正成 RedisCrawlSpider修正配置文件settings指定利用可以被共享的管道:ITEM_PIPELINES = { 'scrapy_redis.pipelines.RedisPipeline': 400 }指定调度器:增加了一个去重容器类的配置,浸染是用Redis的set凑集来存储要求的指纹数据,从而实现要求去重的持久化DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"利用scrapy-redis组件自己的调度器SCHEDULER = "scrapy_redis.scheduler.Scheduler"配置调度器是否要持久化,也便是当爬虫结束了,要不要清空Redis中要求行列步队和去重指纹的set。
如果是True,就表示要持久化存储,就不清数据,否则清空数据SCHEDULER_PERSIST = True指定redis做事器redis干系操作配置:配置redis的配置文件:linux或者mac:redis.confwindows:redis.windows.conf打开配置文件修正:将bind 127.0.0.1进行注释或删除关闭保护模式:protected-mode yes改为no结合着配置文件开启redis做事redis-server 配置文件启动客户端:redis-cli实行工程:scrapy runspider xxx.py向调度器的行列步队中放入一个起始的url:调度器的行列步队在redis的客户端中lpush xxx www.xxx.com爬取到的数据存储在了 redis 的 proName:items 这个数据构造中14. 增量式爬虫观点:监测网站数据更新的情形,只会爬取网站最新更新出来的数据。
剖析:指定一个起始url基于CrawlSpider获取其他页码链接基于Rule将其他页码链接进行要求从每一个页码对应的页面源码中解析出每一个电影详情页的URL核心:检测电影详情页的url之前有没有要求过将爬取过的电影详情页的url存储存储到redis的set数据构造对详情页的url发起要求,然后解析出电影的名称和简介进行持久化存储九、补充——异步编程

为什么要讲?

这一部分的知识点不太随意马虎学习(异步非阳塞、 asyncio)异步干系话题和框架越来越多,例如:tornado、fastapi、django 3.x asgi、aiohttp都在异步→提升性能

如何讲解?

第一部分:协程第二部分:asyncio模块进行异步编程第三部分:实战案例1. 协程

协程不是打算机供应,程序员人为创造。

协程( Coroutine),也可以被称为微线程,是一种用户态内的高下文切换技能。
简而言之,实在便是通过一个线程实当代码块相互切换实行。

def func1(): print(1) ... print(2)def func2(): print(3) ... print(4)func1()func2()

实现协程的集中方法:

greelet,早期模块yield关键字asyncio装饰器(py3.4及往后版本)async、await关键字(py3.5及往后版本)(1)greenlet实现协程

pip install greenlet

from greenlet import greenletdef func1(): print(1) gr2.switch() #切换到func2函数 print(2) gr2.switch() #切换到func2函数,从上一次实行的位置连续向后实行def func2(): print(3) gr1.switch() #切换到func1函数,从上一次实行的位置连续向后实行 print(4)gr1 = greenlet(func1)gr2 = greenlet(func2)gr1.switch() #去实行func1函数