发布时间:2025-12-09 19:58:42 浏览次数:4
网络爬虫:就是模拟客户端发送请求,获取响应数据,一种按照一定的规则,自动地抓取万维网上的信息的程序或者脚本
爬虫分类:
爬虫的矛与盾:
爬虫和浏览器的区别:
浏览器:
浏览器向百度服务器发送请求 --> http 😕/www.baidu.com
百度服务器发送数据–> 获取响应数据,进行渲染 -->给用户看
爬虫:
向百度服务器发送请求–> http 😕/www.baidu.com
百度服务器发送数据–> 获取响应数据,并保存数据
URI全称:Uniform Resource Identifier (统一资源标识符)
URL全程:Universe Resource Locator (统一资源定位符) , URL是URI的子集
举例说明: https://github .com/favicon.ico 这是GitHub网站的图标链接
访问协议https · 访问路径github.com · 资源名称favicon.ico
hypertext–超文本 网页就是超文本解析而成的,网页的源代码就是HTML代码
HTTP:Hyper Text Transfer Protocol (超文本传输协议),用于从网络传输超文本数据到本地浏览器的传送协议,能保证高效而准确的传输超文本文档
HTTPS:Hyper Text Transfer Protocol over Secure Socket Layer (以安全为目标的HTTP通道),就是HTTP的安全版
就是服务器与客户端进行数据交互的一种形式。
常用请求头信息:
常用响应头信息:
Content-Type:服务器响应回客户端的媒体类型信息,例如html、json、gif
加密方式:
由服务端返回给客户端,分为三部分。响应状态码(Response Status Code),响应头(Response Headers),响应体(Response Body)
1.状态码:
200--成功400--错误请求403--禁止访问404--找不到请求的网页502--错误网关503--服务器目前无法使用504--网关超时2.响应头:响应头,Server、Cookies等信息
3.响应体:请求网页的时候,返回的使HTML代码,请求图片的时候响应的使二进制数据
title标签使网页的标题 · body是正文内容
p是网页的区块标签,id=“ ” id的内容在网页中是唯一的 class=“ ” 属性标记
HTML文档是树结构,节点树
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gKtR4Fg8-1673159360178)(C:%5CUsers%5C%E9%93%B6%E6%99%97%5CAppData%5CRoaming%5CTypora%5Ctypora-user-images%5C1632450058735.png)]
1.beautifulsoup的标签不规范,无法识别定位
2.xpath的标签嵌套找不到,定位不到
3.js的渲染
使用HTTP请求库得到的源代码和浏览器中看到的数据不一样 ,是因为js会改变HTML的结点,向其添加内容,最后得到浏览器的页面
4.不要在Elements的选项卡中直接查看源码,那里的代码给渲染过了,要在network中查看
HTTP的特点之一:无状态
指HTTP协议对事务处理是没有记忆能力的,也就是说服务器不知道客户端是什么
在服务端----网站的服务器
在客户端也就可以理解为浏览器端
浏览器在下次访问网页时会自动附带上cookies发送给服务器,服务器通过识别Cookies并鉴定出时哪个用户,然后再判断用户是否是登陆状态进而返回对应的响应
1.会话cookies:
cookies放在浏览器内存里,浏览器关闭之后该cookies失效
2.持久cookies:
浏览器关闭,cookies不会消失,cookies会保存会话ID信息到硬盘上,再次打开浏览器,仍然能够找到原来的会话ID,依旧可以处于保持登录状态。
恰恰是这样,这需要服务器为会话机制设置一个失效时间,距离客户端上一次书院会话的时间超过失效时间,服务器就会删除会话以节省空间
代理–>代理服务器,proxy server
功能:代理网络用户去取得网络信息
理解:代理服务器就是在本机和服务器之间的一个桥,本机向代理发请求,然后代理再向服务器发请求,回来的响应也是经历这样的过程,web服务器无法识别我们的真实IP,从而实现IP伪装
有4个模块
request: HTTP请求模块
error: 异常处理模块,如果出现错误,我们可以捕获异常,然后进行重试或者其他操作,保证程序不会意外终止
parse: 工具模块,提供URL的处理方法
robotparse: 识别网站的robot.txt
requests是一个优雅而简单的Python Http请求库,作用是发送请求获取响应数据
使用3步骤:
导入模块
发送get请求,获取响应
从响应中获取数据
import requestsget(url= ,param= ,headers=)url:请求的路由param:要访问抓取的参数,用抓包工具用网页上抓取,有些固定的参数自己可以更改,注意数 据类型是strheaders:User-Agent,抓包工具抓一次就好了timeout:超时时间,过了这个时间服务器没响应,就返回失败post(url= ,headers= ,data= )注意和get区分response = requests.get('http://www.baidu.com')response.status_code //返回请求状态response.encoding = 'utf-8'# print(response.text)#content :获取响应的二进制数据,图片、图标response.text = response.content.decode() # 默认utf-8 ,如果是gbk ,decode(encoding = ‘gbk’)text(字符串),content(二进制) ,json(对象)UA= User-Agent(请求载体的身份标识)
UA伪装:门户网站的服务器会检测对应请求的载体身份标识。如果是某一款浏览器就是一个正常的请求,如果不是就是爬虫,是不正常的请求,服务端可能会拒绝请求,是拿不到数据的。
为了避免被拒绝,爬虫需要进行UA伪装:让爬虫对应的身份标识伪装成某一个浏览器
将 User-Agent封装到一个字典中:
headers={
’ User-Agent‘ = ‘ 网站上的 User-Agent对应的字符串’
}
response.json() //拿json数据filename= input+'.json'fp= open('./douban.json','w', encording='utf-8')dump.(filename, fp= fp, ensure_ascii=False)破解封IP这种反扒机制,突破自身IP访问的限制,隐藏自身IP不会被封。 什么是代理服务器:作为一个中转服务器,我们的IP发给代理,代理再发IP给服务器,我们的IP地址不会别封。
代理相关网站:快代理、西祠代理、www.goubanjia.com代理ip类型:http:应用到http协议对应的url中https:应用到https协议对应的url中代理ip的透明度:-透明:服务器知道该次请求使用了代理,也知道真实的ip-匿名:知道使用了代理,不知道真实的ip-高匿:不知道使用了代理也不知道真实的ip例如:proxy = '121.230.211.41'如果要用户名: proxy = usernamme:password@'121.230.211.41'proxies={'HTTP/HTTPS':'ip地址','HTTP':'http'+proxy}page_text= requests.get(url=url,headers=headers,proxies=proxies).text数据解析:Beautifulsoup,正则表达式,xpath
概念:解析的局部标签对应的属性中存储的数据值进行提取
解析图片:
url= '...'img_data= requests.get(url=url).contentwith open('./giutu.jpg','wb') as fp:fp.write(img_data)bs4数据解析:
BeautifulSoup 对象:代表要解析整个文档树,它支持 遍历文档树 和 搜索文档树 中描述的大部分的方法
它的作用就是从 HTML 和 BeautifulSoup 对象的创建
fp=open('.html','r',encording='utf-8')soup= BeautifulSoup(fp,'lxml')soup.tagName():返回的是html中第一次出现的tagName,tagName就是标签名find的作用就是找标签
步骤:
find(self ,name=, attrs={}, recursive=True, text= ,**kwargs )
find_all(name , attrs , recursive , text,**kargs)
参数:
返回:查找到的第一个元素对象
soup = BeautifulSoup('<html>data</html>', 'lxml') # BeautifulSoup 会自动修正代码print(soup)#要准备html的文档html='''...'''soup= BeautifulSoup(html , 'lxml')print(soup)#查找title和a标签title= soup.find('title')a= soup,find('a') #属性定位soup.find('p',class_=' song') 两个属性定位,标签+属性(可以是id或者attr)#查找所有的asoup= find_all('a')print(a+title)soup.select('.属性名') . 代表class_ id/soup.select('.tang >ul>li>a')[0] 标签层级选择器,大于号表示一个层级,[]表示第几个soup.select('.tang >ul a')[0]空格一次性表示多个层级soup.select('.tang >ul a')[0].text 直接获取文本soup.a['标签名'] 直接提取文本#根据属性进行查找#查找id 为link1的标签a= soup.find(id='link1')a= soup.find(attrs={'id':'link1'})#两种方法print(a)#根据文本内容进行查找text= soup.find(text=' 要查找的内容')#Tag对象print('标签名',a.name)print('标签所有的属性',a.attrs)print('标签文本内容',a.text)Tag可以获取标签的属性和文本原理:实例化一个etree的对象,且需要将被解析的页面的源码数据加载到该对象中,调用xpath对象的xpath方法获取标签的定位和内容捕获
如何实例化etree对象:
pyquery选择返回的结果都是pyquery类型的,可以进行类型转换
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wVJYpUAU-1673159360180)(C:%5CUsers%5C%E9%93%B6%E6%99%97%5CAppData%5CRoaming%5CTypora%5Ctypora-user-images%5C1635860179367.png)]
或者:a.attr.href
a.text() 返回的是该节点的所有的文本,字符串类型
a.html() 返回当前节点文本
1.addClass 和removeClass
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-T6qQT0dQ-1673159360181)(C:%5CUsers%5C%E9%93%B6%E6%99%97%5CAppData%5CRoaming%5CTypora%5Ctypora-user-images%5C1635860890528.png)]
2.attr方法对属性进行操作
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-poL86Tnx-1673159360181)(C:%5CUsers%5C%E9%93%B6%E6%99%97%5CAppData%5CRoaming%5CTypora%5Ctypora-user-images%5C1635861027622.png)]
3.text()和html()
4.append、empty、prepend方法
UnicodeError是在处理字符串时出现的错误,分为两个子异常类,UnicodeEncode和UnicodeDecodeError
处理这个问题需要使用 encode() 和 decode()方法
encode和decode均可接收encoding与errors两个参数,用来指定编码、解码的错误和出现错误的时候的反应#注意!解码和解码前后的字符串的长度可能不同,因为二者的长度意义不同。对于Unicode长度为其中字符的长度,str的长度取决于bytes的数目encode :将unicode字符串翻译成bytes,也就是str对象decode :相反,将bytes翻译为原有的Unicode字符串unicode字符串就是str-> 'hello world'bytes字符串->b'hello world'Encoding Error: UnicodeDecodeError
报错原因:由于指定的编码格式不足以编码指定unicode字符串中的某些字符,说明使用的时错误的编码格式
>>>unicode_seq.encode('ascii')Traceback (most recent call last):File "<stdin>", line 1, in <module>UnicodeEncodeError: 'ascii' codec can't encode character u'\xf8' in position 13: ordinal not in range(128)#ascii无法编码u'\xf8'字符Decoding Error:UnicodeDecodeError
报错原因:你指定的编码格式无法解码该字符串
>>> wrong_seq.decode('utf-8')Traceback (most recent call last):File "<stdin>", line 1, in <module>File "/usr/local/Cellar/python/2.7.13/Frameworks/Python.framework/Versions/2.7/lib/python2.7/encodings/utf_8.py", line 16, in decodereturn codecs.utf_8_decode(input, errors, True)UnicodeDecodeError: 'utf8' codec can't decode byte 0x89 in position 1: invalid start byte#utf-8无法解码byte流解决报错的思路:
思路一:Unicode编码解码的顺序程序的内部是Unicode字符串,程序外部是bytes字符串,所以在程序的入口解码读取到的数据,在程序的出口编码数据[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ucquxuNt-1673159360182)(C:%5CUsers%5C%E9%93%B6%E6%99%97%5CPictures%5CSaved%20Pictures%5C70)]
思路二:了解被处理的数据是那种类型两个方法:1.type()方法查看这个数据的类型 2.repr()方法查看它的Unicode字符串到底是什么 #乱码尝试方法1中文转Unicode编码:text = '中国'result = text.encode('unicode_escape')Unicode转中文:result = u_str.decode('unicode_escape')# 乱码尝试方法21.图片:img_name = img_name.encode('iso-8859-1').decode('gbk')2.响应数据:response.encoding = 'utf-8'[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NQN8S858-1673159360183)(C:%5CUsers%5C%E9%93%B6%E6%99%97%5CAppData%5CRoaming%5CTypora%5Ctypora-user-images%5C1632404127737.png)]
1.^符和&符的使用,不要单独使用,结合文本 例如:^He(\d+)Demo$
2.省略的部分书写(非贪婪模式):(.*?)
3.遇到不规则的字符:加一个反斜杠 例如:\(百度)www… 避免开始匹配不到
字符匹配:
\d:代表任意数字 \D:代表不是数字的(大写的字母一般是和小写的唱反调)
\w:代表字母,数字,下划线,也就是a-z,A-Z,0-9,—。 \W:不是字母数字下划线的
\n:代表一个换行
\r:代表一个回车 \f:代表一个换页 \t:代表一个tab
\s:代表所有的空白字符(包括换行回车换页tab) \S:不是空白的字符
\A:代表字符串的开始 \Z:代表字符串的结束
^:匹配字符串的开始位置 &:匹配字符串的结束位置
\b:匹配一个单词的边界 \B:匹配非单词边界
|:匹配两边的表达式 +() 要想匹配的不多不少刚刚好,就必须用^和&来规定开始结束
星号:匹配前面的字符串零次或多次 例如:zo* 能匹配z、zo、zoo
+:匹配前面的字符串一次或多次 例如:zo+能匹配zo、zoo但不能匹配z
?:匹配前面的字符串零次或一次 例如:do(es)?可以匹配do或者does
[n]:n是一个非负整数,匹配确定的n次 例如o[2]必须匹配到2个o,bob就不能匹配到
[n,]:就是至少匹配n次,可以多不能少 [n,m]:最少匹配n次,最多匹配m次
. : 匹配 \r \n 之外的任何的单个字符
[…]:表示一个范围内的字符 例如[a-z]就是a-z间任意一个字符 [^]唱反调
{n}:匹配在{n}前面的东西 n次
()括号可以作为一个分组 ,括号括起来的内容可以作为中间变量记录下来,要想记录下来并使用把后面的那一部分也括起来然后写 (\1)数字就是第几个括号
括号一多容易混,所以用信息加以定义 (?p=< 定义名字>) 例子:(?p=< key1>)
首先导入模块re
校验数字:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mmrJ6avx-1673159360184)(C:\Users\银晗\AppData\Roaming\Typora\typora-user-images\1611386525311.png)]
校验字符的表达式:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-z1rOmeqR-1673159360184)(C:\Users\银晗\AppData\Roaming\Typora\typora-user-images\1611386578655.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RYEO8mCp-1673159360185)(C:\Users\银晗\AppData\Roaming\Typora\typora-user-images\1611386615056.png)]
re模块的方法:
循环所有匹配:re.findall()
re.findall(pattern, string , flags=0)
pattern=正则表达式 string=匹配的字符串
res=re.findall(r‘ r[ua]n’, ‘run ran ren’ ) print(res) 输出run 和ran
注意如果要找单独的单词,请在要匹配的单词首尾各空一个空格
re.match(正则表达式,要匹配的字符串) result=re.match() 从左到右匹配字符串,只返回匹配到的
如果match匹配到数据用result.group() 返回数据
替换匹配内容:re.sub()
re.sub( 待替换的字符串,要替换上的字符串,原来整个字符串)
过滤掉网页中那些不需要的符号:re.sub( r‘<.+?>’, ,s)或者(r’</? \w+ >‘ )
实例操作:re.sub(r “ http://.+?/)”,lambda x: x.group(1),s) 用匿名函数输出来替换
re.search(r’‘ 搜索内容’, ‘匹配内容 ’) :与match不同终端无法继续
分裂内容:re.split()
res=re.split(r ‘ ,;.\ \(分裂标志)’ , ‘a,b;c.d\e’) 就是把,;. \之间的值全部分裂开输出a,b,c,d,e
返回的是列表
包装正则表达式:re.compile()
compile_re=re.complie(r’ r[ua]n ’) 就相当于等效了一下
res=compile_re.findall(‘ run ran ren’)
贪婪模式:只有不设置限制,系统默认一直往后找
用括号括起要关闭贪婪模式的表达式 加上一个?
json模块是Python自带的模块,用于json与python数据之间的相互转换
json.load()方法:
把json字符串,转换为python数据
json_str=‘’‘ … str …’‘’
rs = json.loads(json_str)
读取文件:
with open(‘data.json’) as fp:
python_list = json.load(fp)
python 数据转换为json数据:
json.dumps(obj) 转换为json字符串
json_str = json.dumps(rs, ensure_ascii=Flase) #有中文就要指定ensure_ascii=Flase
python 数据转换为json数据写入文件:
with open('data.json', 'w') as fp:json.dump(rs, fp, ensure_ascii=Flase)1.json文件存储
with open(file = 'result.txt',mode='a',encording='utf-8') as f:f.write(json.dumps(content,ensure_ascii=False)+'\n',indent=2)#indent表示缩进2个空格2.csv文件存储
import csvwith open('data.csv','w') as csvfile:writer.writerow(['','','']) //写入每行的数据不过我还是建议用pandas中转保存!
3.连接MySql数据库:
import pymysqldb = pymqsql.connect(host='localhost',user='root',password='20020520zyh,port=3306')cursor = db.cursor() //获得mysql的操作游标// 用execute执行sql语句cursor.execute('SELECT VERSION()')//获取版本data = cursor.fetchone()print('DATABASE version:',data)cursor.execute("Create database spiders default character set utf-8")db.close()目的:在爬虫中使用异步实现高性能的数据的爬取
#同步爬虫,单线程,get()很慢,是一个阻塞的方法response=requests.get(url=url,headers=headers)if response.status_code == 200:#判断是否有响应return response.content异步爬虫方式:
多线程,多进程:可以为相关阻塞的操作单独开启线程或者进程,不会等。但是不能无限制的开启多线程
线程池、进程池:可以降低系统对进程或者线程创建和销毁的一个频率,提升效率。但是池中线程数量有上限
from multiprocessing.dummy import Poolname_list=['aa','bb','cc','dd']def get_page(str):{print(str)}#实例化线程对象pool=Pool(4)#四个对象pool.map(get_page,name_list)#map(阻塞的方法,对象列表)单线程+异步协程:
基本操作快捷键:
之前动态加载数据,需要用抓包工具,抓XHR中的参数,用param参数列表进行爬取,selenium更方便
selenium是基于自动化的一个模块
基本使用:
from selenium import webdriverfrom selenium.webdriver.common.by import Byfrom selenium.webdriver.common.keys import Keysfrom selenium.webdriver.support import expected_condition as ECfrom selenium.webdriver.support.wait import WebDriverWaitbrowser = webdriver.Chrome()try:browser.get('https://www.baidu.com') #访问页面input = broswer.find_element_by_id('kw') #找到输入框input.send_keys('Python') #输入input.send_keys(Keys.ENTER) #按回车wait = WebdriverWait(broswer,10) #等待10swait.until(EC.presence_of_element_located((By.ID),'content_left'))print(broswer.current_url,broswer.get_cookies(),broswe.page_source())expect Expection as e:print(e)Fianlly:broswer.close()自动化代码:
发起请求:get()
broswer= webdriver.Edge()
标签定位:find_element_by_选择器()
标签交互:send_keys()
执行js程序:excute_script(‘jsCode’)
前进、后退:back()、forward()
关闭:quit()
点击:click()
清空:clear()
获取cookies:
异常处理
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-icAPXOl9-1673159360185)(C:%5CUsers%5C%E9%93%B6%E6%99%97%5CAppData%5CRoaming%5CTypora%5Ctypora-user-images%5C1632557905053.png)]
单个标签:find_element_by_选择器()
选择器:id name xpath link_text tag_name class_name css_selector
多个标签:find_elements_by_选择器()
输入:send_keys(‘内容’)
前进、后退:back()、forward()
关闭:quit()
点击:click()
清空:clear()
通过page_source可以获取网页的源代码,接着可以用解析库解析了,但是selenium提供了选择节点的方法
例:
bro = webdriver.Chrome()url = 'https://zhihu.com'bro.get(url)input = bro.find_element_by_class_name('zu-top-add-question')print(input.text)获取结点的信息主要通过属性:
input.idinput.locationinput.tag_nameinput.size网页中有一种节点叫做iframe , 也就是子Frame,相当于页面的子页面,它的结构和外部页面一致,但是selenium打开页面之后默认在父级Frame里面操作,不能获取子Frame里面的节点,这时候需要swith_to.frame()来切换Frame
browser.get(url) browser.switch_to.frame('iframresult ’) browser.switch_to.parent_frame()隐式等待:Selenium没有找到节点,就等着直到超出设定时间,则抛出异常
browser=webdriver.Edge()browser=implicitly_wait(10) 等待10s显式等待:指定最长等待时间,如果在规定时间内加载出来就返回,超时则抛出异常
wait = WebDriverWait(browser,10)input = wait.until(EC.presence_of_element_located(By.ID,'q'))[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QZJn26uG-1673159360186)(C:%5CUsers%5C%E9%93%B6%E6%99%97%5CAppData%5CRoaming%5CTypora%5Ctypora-user-images%5C1632556633791.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cDgiFWOM-1673159360187)(C:%5CUsers%5C%E9%93%B6%E6%99%97%5CAppData%5CRoaming%5CTypora%5Ctypora-user-images%5C1632556869868.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lJ8SCOOf-1673159360187)(C:%5CUsers%5C%E9%93%B6%E6%99%97%5CAppData%5CRoaming%5CTypora%5Ctypora-user-images%5C1632556633791.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Wbvz4nGt-1673159360188)(C:%5CUsers%5C%E9%93%B6%E6%99%97%5CAppData%5CRoaming%5CTypora%5Ctypora-user-images%5C1632556869868.png)]
创建工程:scrapy startproject 文件名
cd 文件名
在spiders子目录中创建一个爬虫文件:scrapy genspider first www.xxx.com
执行工程:scrapy crawl 文件名
进入setting:进行UA伪装,USER_AGENT='...'把ROBOTSTXT_OBEY = True 改成Flase 添加LOG_LEVEL='ERROR' import scrapyclass FirstSpider(scrapy.Spider):name = 'first' # 爬虫文件的名称# allowed_domains = ['www.xxx.com'] 允许的域名,start_urls中哪些url可以进行请求,不过一般不用这种机制start_urls = ['https://www.baidu.com/', 'https://www.sogou.com/'] # 起始的url列表,可以有多个url# 用于数据解析,response参数表示就是start_urls列表的url请求成功后对应响应的对象def parse(self, response):pass全站数据爬取的方式:基于spider:手动请求;基于CrawlSpider
使用:创建爬虫文件,scrapy genspider -t crawl 文件名 www.xxx.com
#链接提取器link = LinkExtractor(allow=r'type=4&page=\d+')#匹配到了当前页面的链接#去起始url中,根据指定规则,提取链接 allow="正则表达式"rules = (#规则解析器对象Rule(LinkExtractor(allow=r'Items/'), callback='parse_item', follow=True),)全站数据爬取过程:
道类,养成习惯
4.进入setting更改配置:
开启管道类,并修改类名,改为自定义的管道类名
末尾增加一行存储路径
IMAGES_STORE = ‘./imgs_han’
全站数据爬取的方式:基于spider:手动请求;基于CrawlSpider
使用:创建爬虫文件,scrapy genspider -t crawl 文件名 www.xxx.com
#链接提取器link = LinkExtractor(allow=r'type=4&page=\d+')#匹配到了当前页面的链接#去起始url中,根据指定规则,提取链接 allow="正则表达式"rules = (#规则解析器对象Rule(LinkExtractor(allow=r'Items/'), callback='parse_item', follow=True),)全站数据爬取过程:
目的:在爬虫中使用异步实现高性能的数据的爬取
#同步爬虫,单线程,get()很慢,是一个阻塞的方法response=requests.get(url=url,headers=headers)if response.status_code == 200:#判断是否有响应return response.content异步爬虫方式:
多线程,多进程:可以为相关阻塞的操作单独开启线程或者进程,不会等。但是不能无限制的开启多线程
线程池、进程池:可以降低系统对进程或者线程创建和销毁的一个频率,提升效率。但是池中线程数量有上限
from multiprocessing.dummy import Poolname_list=['aa','bb','cc','dd']def get_page(str):{print(str)}#实例化线程对象pool=Pool(4)#四个对象pool.map(get_page,name_list)#map(阻塞的方法,对象列表)单线程+异步协程:
登录成功之后,重新发起详情页的请求后,页面会重新回到登录页面,因为cookies的原因
1.cookies值的来源来自哪里?来源上一次对网页发起请求后生成的
2.使用session对象:
1.可以进行请求的发送
2.如果请求过程中产生了cookie,则该cookie会被自动储存,即携带在session对象中
1.url错误
2.登陆后的参数列表错误,参数列表除了icode其余都要写成键值对的形式,因为icode每次都会变化
协程不是计算机提供,程序员是认为创造。
也叫微线程,是一种用户态内的上下文切换技术,简而言之,其实就是通过,一个线程实现代码块相互切换。
在一个协程中如果遇到IO等待时间,线程不会等待,利用空闲的时候再去干的别的事。
实现协程的方法:
遇到IO阻塞就自动切换代码块执行,遇到耗时的操作要等待的时候,就立即切换下一个代码块,加快速度
协程函数:asyncio def 函数名
协程对象: 执行协程函数产生的协程对象
asyncio def func():print(...)result = func()asyncio.run(result)| get_event_loop() | 开启事件循环 |
| run_until_complete(task列表) | 执行任务 |
| await() | 等待事件结束,返回结果 |
| run() | 运行 |
| ensure_future() | 创建future对象 |
理解为一个死循环
1.去检测任务列表,
2.如果任务可执行,就去执行,
3.如果不可执行,就忽略跳过,
4.如果有任务执行完成,就从任务列表中移除
代码操作:
import asyncio#生成一个事件循环loop = asyncio.get_event_loop()#将任务循环放到任务列表loop.run_until_complete(任务)await + 可等待对象(协程对象、Future对象、Task对象)
#单个协程对象async def func():print('..')result = await asyncio.sleep(2) #等待这个io结束,并返回结果print(result)asyncio.run(func()) #多个协程对象async def others():print('start')await asyncio.sleep(2)print('end')return 2async def func1():print('..')result1 = await others() #接收返回的2print(result1)async def func2():print('..')result1 = await others() #遇到IO阻塞,会切换到其他的协程但是不会执行result2print(result1)result2 = await others() #接收返回的2print(result2)asyncio.run(func())在事件循环中添加多个任务
Task用于并发调度协程,通过asyncio.create_task(协程对象)的方式创建Task对象,这样可以让协程加入事件循环中等待被调度执行。除此之外,太可以用低级一点的loop.create_task ()或者 ensure_future() 函数,本质上是一样的。
示例代码1:
async def others():print('start')await asyncio.sleep(2)print('end')return 2async def main():print('main开始')task1 = asyncio.create_task(func())#这里特别解释一下,task1中在执行others函数中 await的sleep的操作时遇到阻塞会自动切换到task2去执行,所以输出第一个start然后接着输出第二个starttask2 = asyncio.create_task(func())ret1 = await task1ret2 = await task2asyncio.run(main())实例代码2:
async def others():print('start')await asyncio.sleep(2)print('end')return 2async def main():print('main开始')task_list =[asyncio.create_task(func(),name = '设置task的名字'),asyncio.create_task(func())]print('main结束')done,pending = await asyncio.wait(task_list,timeout=2) #等待任务列表中的任务全部结束#done是已完成的任务的返回值的列表#pending是超过timeout时间问完成的任务列表asyncio.run(main())#另一种写法:task_list =[func(),func(),]#这里会内部自动创建任务,所以列表中就不用创建任务asyncio.run(asyncio.wait(task_list))基于协程,Task继承Future,Task对象内部await结果处理基于Future对象
async def main():#创建当前时间循环loop = asyncio.get_running_loop()#创建future对象的任务fut = loop.create_future()#等待任务循环(Future对象),没有结果则会一直等下去await futasyncio.run(main())实例代码:
async def func(fut):await asyncio.sleep(2)fut.func('666')async def main():#创建当前时间循环loop = asyncio.get_running_loop()fut = loop.create_future()#创建任务,绑定func任务await loop.create_task( func(fut) )data = awiat futprint(data)使用线程池、进程池实现异步操作时用到的对象
from concurrent.futures import Futurefrom concurrent.futures.thread import ThreadPoolExcutorfrom concurrent.futures.process import ProcessExecutordef func(values):time.sleep(1)print(values)#创建线程池pool = ThreadPoolExecutor(max_workers = 5)#创建线程池pool = ProcessPoolExecutor(max_workers = 5)for i in range(10):fut = pool.submit(func , i )#创建10个线程,但线程池容量为5print(fut) import timeimport asyncioimport concurrent.futuresdef func1():time.sleep(2)return "bb"async def main():loop = asyncio.get_running_loop()#首先,内部调用THreadPoolExecute方法去线程池中执行func函数,并返回一个concurrent.futures.Future对象#然后,调用asyncio.wrap_future对象将concurrent.futures.Future对象包装成asyncio.Future对象,因为concurrent.futures.Future对象不支持await方法,所以需要包装fut = loop.run_in_executor(None, func1)result = await futprint('default thread pool' ,result)#使用线程池with concurrent.futures.ThreadPoolExecutor() as pool:result = await loop.run_in_execute(pool,func1)#使用进程池 with concurrent.futures.ProcessPoolExecute() as pool:result = await loop.run_in_execute(pool,func1)asyncio.run(main())实战案例:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Hx7C5Yol-1673159330044)(C:%5CUsers%5C%E9%93%B6%E6%99%97%5CAppData%5CRoaming%5CTypora%5Ctypora-user-images%5C1636385289648.png)]
爬虫内部有三个线程,下载一个图片的同时会执行其他的线程,即下载另一张图片
自动打开和关闭协程函数
定义_ aenyer ()和 _ aexit _ _()方法对async with 语句中的环境进行控制
class AsyncManager:def __init__(self):self.conn = conn#第2步:执行函数async def do_something(self):return 666#第1步:先执行这里async def __aenter__(self):#异步连接数据库self.conn = await asyncio.sleep(1)return selfasync def __aexit__(self,exc_type,exc,tb):await asyncio.sleep(1)async def func(): asynci with AsyncManager() as f:result = await f.do_something()print(result)asyncio.run(func())[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ytlugbKF-1673159330045)(C:%5CUsers%5C%E9%93%B6%E6%99%97%5CAppData%5CRoaming%5CTypora%5Ctypora-user-images%5C1636416125977.png)]
")
#网络IO操作,遇到阻塞会自动切换任务cur = await conn.cursor()#网络IO操作,遇到阻塞会自动切换任务await cur.execute("SELECT HOST ,USER FROM user")await cur.execute('select* from exp ...')#网络IO操作,遇到阻塞会自动切换任务result = await cur.fetchall()print(result)#网络IO操作,遇到阻塞会自动切换任务await cur.close()conn.close()print("结束")task_list = [
execute(‘47.93.41.197’,“root12345”),
execute(‘23.23.67.346’,“root12345”)
]
[外链图片转存中…(img-ytlugbKF-1673159330045)]