爬虫学习
Requests模块初识
一小爬虫初见requests:
url='https://www.sogou.com/web?query=周杰伦'
resp=requests.get(url)
print(resp.text)
步骤:
先找到需要爬取的url,然后用requests的get方法获取url的响应response就可得到该页面的数据
User-Agent的使用:
先看一段简短的爬虫代码:
kw=input("")
url=f'https://www.sogou.com/web?query={kw}'
resp=requests.get(url)
print(resp.text)
但是运行出来啥也没有,很明显只能是被反爬拦截了。
因此我们要做一个伪装,我们之所以被拦截是因为该服务器把我们给认出来了/(ㄒoㄒ)/~~,直接拒之门外好吧!
但是莫得关系我们直接摇身一变伪装成浏览器,所以咱们就加一个请求头中的User-Agent
headers={
"User-Agent": 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.107 Safari/537.36'
}
然后再在获取响应的过程中加上这个请求头就行了
resp=requests.get(url,headers=headers)
就大功告成了
爬取百度翻译的练习:
这是一个客户端渲染的一个网站因此第一次请求中确实是没有数据只有html骨架的
因此我们得去找第二次请求中的数据,而第二次一般是在XHR类型中
然后一个一个找就找到了第二次请求为sug
然后找到url复制下来爬取即可,但是这是是有输入的所以显然应当是一个post请求,所以这里的接受响应有所区别,还有data参数接受输入数据
word=input("please input word you want to translate:")
data={
"kw":word
}
resp=requests.post(url,data=data)
这样就大功告成了
re模块
正则表达式回顾:
元字符: 具有固定含义的特殊符号 常⽤元字符
. 匹配除换⾏符以外的任意字符
\w 匹配字⺟或数字或下划线
\s 匹配任意的空⽩符
\d 匹配数字
量词: 控制前⾯的元字符出现的次数
贪婪匹配和惰性匹配
\n 匹配⼀个换⾏符
\t 匹配⼀个制表符
^ 匹配字符串的开始
$ 匹配字符串的结尾
\W 匹配⾮字⺟或数字或下划线
\D 匹配⾮数字
\S 匹配⾮空⽩符
a|b 匹配字符a或字符b
() 匹配括号内的表达式,也表示⼀个组
[...] 匹配字符组中的字符
[^...] 匹配除了字符组中字符的所有字符
量词: 控制前⾯的元字符出现的次数
* 重复零次或更多次
+ 重复⼀次或更多次
? 重复零次或⼀次
{
n} 重复n次
{
n,} 重复n次或更多次
{
n,m} 重复n到m次
贪婪匹配和惰性匹配
.* 贪婪匹配
.*? 惰性匹配
最常用的!!!!!
其他正则都不说了,基本不用。。。除了最后两个贪婪匹配与惰性匹配
贪婪指的是尽可能多的去匹配
而惰性是尽可能少的去匹配
这些数据解析的模块基本都是处理超文本文件的,所以我们用几个超文本文件的案例来解释一下这俩正则表达式
str: 玩⼉吃鸡游戏, 晚上⼀起上游戏, ⼲嘛呢? 打游戏啊
reg: 玩⼉.?游戏 此时匹配的是: 玩⼉吃鸡游戏
reg:玩⼉.游戏 此时匹配的是: 玩⼉吃鸡游戏, 晚上⼀起上游戏, ⼲嘛呢? 打游戏
re的匹配规则:
obj = re.compile(r"\d+") #预加载正则表达式
lis=obj.finditer("www.43468.dasd151.dasd185151")#匹配url中html符合的数据
for i in lis:
print(i.group()) #由于finditer匹配是返回的是迭代器所以用group方法得到数据
print(lis)
re中匹配方法如下:
'''
#findall:匹配字符串中所有符合其正则的子字符串并返回列表
lis=re.findall(r'www.*\.com','www.4399.com')
print(lis)
#finditer最常用重要
#finditer :匹配字符串中所有符合其正则的子字符串并返回迭代器
it=re.finditer(r'\d+','www.4399.com.54184')
print(it)
for i in it:
print(i,end="group可去掉数据其他的东西")
print(i.group())
#search,找到一个结果就返回其余与finditer类似
s=re.search(r'\d+','www.4399.com.54184')
print(s.group())
#match从头开始匹配其余与finditer类似
s=re.match(r'\d+','www.4399.com.54184')
print(s.group())
'''
还有一个非常非常重要的一个东西总之re你就必须得用好吧! 呐呐呐就是把匹配的字段取别名以便用group取出是可以只要有别名的那一段数据
s = """
<div class='三国'><span id='10010'>移动</span></div>
<div class='⻄游'><span id='10010'>中国联</span></div>
<div class='⻄记'><span id='10010'>中通</span></div>
<div class='⻄'><span id='10010'>联通</span></div>
<div class='⻄游记'><span id='10010'>中国联通</span></div>
<div class='记'><span id='10010'>中国通</span></div>
"""
obj = re.compile(r"<div class='(?P<Name>.*?)'><span id='(?P<id>.*?)'>(?P<name>.*?)</span>", re.S) #re.S是让.能够匹配换行符防止匹配断掉
ls=obj.finditer(s)
for i in ls:
print(i.group('Name'),i.group('id'),i.group('name'))
结果为
三国 10010 移动
⻄游 10010 中国联
⻄记 10010 中通
⻄ 10010 联通
⻄游记 10010 中国联通
记 10010 中国通
爬取电影天堂电影前列排名的下载地址示例:
该需求需要这几个步骤
定位到2020必看片
从2020必看片中提取到子页面的地址
对子页面的链接地址发送请求,拿到下载地址
而进入url可看到
可知该网站有加密所以我们在得到响应的代码中应当加入
resp=requests.get(domain,verify=False) #verify:取消安全验证
并且除此之外如果执行这段代码得到的是一段乱码,而在这些乱码中能够看到该html的编码方式,所以还需要encoding操作resp.encoding='gb2312'
domain="https://dytt89.com/"
resp=requests.get(domain,verify=False)
print(resp.text)
然后就可以根据前面所讲的re去匹配到子页面地址
注意在使用re中的堕落匹配时最前面的html标签一定是要有代表性的,不然就可能匹配到其他地方去了就得不到数据
得到子页面地址后就可以遍历该地址迭代器然后与domain拼接成子页面url然后继续之前类似的操作去获取电影的名字与下载地址即可
#定位到2020必看片
#从2020必看片中提取到子页面的地址
#对子页面的链接地址发送请求,拿到下载地址
import requests
import re
domain="https://dytt89.com/"
resp=requests.get(domain,verify=False) #verify:取消安全验证
resp.encoding='gb2312'
obj1=re.compile(r'>2022必看热片.*?<ul>.*?</ul>',re.S)
obj2=re.compile(r"<li><a href='(?P<name>.*?)'")
obj3=re.compile(r'<div class="title_all"><h1>(?P<name>.*?)</h1>.*?<div class=player_list>.*?<a href="(?P<download_ref>.*?)">',re.S)
res1=obj1.finditer(resp.text)
for i in res1:
ul=i.group()
res2=obj2.finditer(ul)
res=[]
for i in res2:
child_url=domain+i.group('name')
resp2=requests.get(child_url,verify=False)
resp2.encoding='gb2312'
final_res=obj3.finditer(resp2.text)
for i in final_res:
res.append(i.group('name'))
res.append(i.group('download_ref'))
f=open('movie_ref.txt',mode='w') #写入txt文件
for i in range(0,len(res),2):
f.write(res[i])
f.write(':')
f.write(res[i+1])
f.write('\n')
print(f'第{i//2+1}个爬取完毕')
print('爬取完毕')
bs4模块:
bs4相对于re就简单太多了,不过我在学习的时候就很神奇,跟着文档一起爬那两个网站,就爬了一丢丢就把我ip封了几小时,特么的就只好随便又找了几个网站玩玩!!
bs4的操作步骤
1.依旧是获取指定url中html数据的响应 #解析数据
2.把页面源代码交给BeauSoup处理,生成bs对象 page=BeautifulSoup(resp.text,"html.parser") #指定html解析器,不然会有warning
3.从bs对象中查找数据
bs的查找手段就主要两种
find:(标签,属性=值)只找第一个
findall:(标签,属性=值)全找
其主要就是利用标签作为参考去找到想要的数据就行了
值得注意的是属性如class就与python关键字所撞上了
处理方法再属性后加_ 或者写成字典形式
然后看实际用法
得到结果
大致就是如此
这上述都是老师的文档,别问我为啥不自己写一遍,这网站从服务端渲染变成了客服端渲染,虽说这样难度直线下降but不能练手呀
爬取美女图片示例:
这个同样没有用老师讲解爬取的网址,别问问就是ip又没封了很神奇就,
于是乎我就找了个美女更美的网址,机智如我
示例代码如下:
#1.拿到主页面的源代码.然后提取到子页面的链接地址,href
#⒉,通过href拿到子页面的内容,从子页面中找到图片的下载地址img ->src#3.下载图片
import requests
from bs4 import BeautifulSoup
import time
url='https://xw.qq.com/amphtml/20220407A0C50300'
resp=requests.get(url)
resp.encoding='uff8'
page=BeautifulSoup(resp.text,"html.parser")
page=page.find("article",class_="article-main fold").findAll("amp-img")
c=1
for a in page:
scr=a.get('src')
img_resp=requests.get(scr)
f=open(f'{c}.jpg','wb')
c+=1
f.write(img_resp.content)
print("over!",scr)
time.sleep(3)
print("over!!!")
这代码唯一需要补充的就是直接从一段httml中去获取src后的链接(ref也如此)就是scr=a.get('src')
还有就是下载图片则是使用request获取img_resp=requests.get(scr)
接着就写入f.write(img_resp.content)
就大功告成咯
godness好吧
xpath模块:
标签就跟文件一样,都有不同的层级,而xpath就是像找文件路径一样一层一层进入找到所需要的数据,而xpath而是相较而言比较简单的,唯一比较麻烦的点就是眼睛去看一层一层的进入标签,难不难,费眼睛。
xpath用法依旧简单
⽤法:
- 将要解析的html内容构造出etree对象.
- 使⽤etree对象的xpath()⽅法配合xpath表达式来完成对数据的提 取
如
tree=etree.parse('1.html')
result=tree.xpath('/html//li[1]/text()')
示例:
这是一个html文件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Title</title>
</head>
<body>
<ul>
<li><a href="http://www.baidu.com">百度
</a></li>
<li><a href="http://www.google.com">⾕
歌</a></li>
<li><a href="http://www.sogou.com">搜狗
</a></li>
</ul>
<ol>
<li><a href="feiji">⻜机</a></li>
<li><a href="dapao">⼤炮</a></li>
<li><a href="huoche">⽕⻋</a></li>
</ol>
<div class="job">李嘉诚</div>
<div class="common">胡辣汤</div>
</body>
</html>
在xpath中有几个用法
1./*表示匹配该节点下所有字节点
2.//表示该节点下所有子节点以及子节点的子节点......所以他更猛
3.在路径后可以加索引不过是从1开始,如/div[1]
4.想要拿到该节点下的数据则还需添加/text()
5.还可以对标签属性选取,如res=tree.xpath(//div[@class='job'])
这个@就是对标签进行局部解析,而且还可以单独取出属性的内容res=tree.xpath(//div/@class)
即可
- 局部解析:对所选择的数据块再进行第二次解析,第一次是为了定位一个大的区域,把范围变小。在局部解析的时候需要在xpath的路径中加当前路径即./
xpath示例爬取八戒网
代码示例:
import requests
from lxml import etree
#拿到数据
url = "https://beijing.zbj.com/search/f/?type=new&kw=saas"
resp = requests.get(url)
#数据解析
html=etree.HTML(resp.text)
#拿到每个服务商div总集
divs = html.xpath("/html/body/div[6]/div/div/div[2]/div[5]/div[1]/div")
for div in divs:
prices=div.xpath("./div/div/a/div[2]/div[1]/span[1]/text()")
titles=div.xpath("./div/div/a/div[2]/div[2]/p/text()")
print(titles)
print(prices)
需要讲解的就是如何利用抓包工具找到我们想要的数据在哪里
首先就是点击这儿箭头
然后我们去点击我们想要爬取的地区咱们就可在Elements中看到那片超文件文本了
然后定位到了就可以复制那段代码的xpath了,然后就可以慢慢一层一层找了
总结
这几个模块刚入手还是比较简单的,逻辑清晰没啥难点,主要就是刚开始弄我被好几个网站封了几小时ip,所以我在网上找了些许方法避免,
一是请求过快过多,所以需要在循环爬取中加入延时
还有就是可能request的连接数过多而导致Max retries exceeded
所以在header中不使用持久连接
'Connection': 'close'