通用爬虫
抓取网站系统重要组成部分,抓取的是一整张页面数据。
聚焦爬虫
建立在通用爬虫的基础之上,抓取页面中特定的局部内容。
增量式爬虫
检测网站中数据更新的情况,只会抓取网站中最新更新出来的数据。
window默认是gbk
open(encoding="utf-8")
爬虫:通过编写程序来获取到互联网上的资源
读取网页的页面源代码
from urllib.request import urlopen
url = "http://www.baidu.com/"
resp = urlopen(url)
with open("baidu.html",mode="w",encoding="utf-8") as f:
f.write(resp.read().decode("utf-8"))
print("over!")
resp.close()#关闭resp
服务器渲染:在服务器那边直接把数据和html整合在一起,统一返回给浏览器
客户端渲染:
第一次请求只要一个html骨架,第二次请求拿到数据,进行数据展示。
在页面源代码中,看不到数据。
熟练浏览器抓包工具!
HTTP是一个客户端终端(用户)和服务器端(网站)请求和应答的标准(TCP)。通过使用网页浏览器、网络爬虫或者其它的工具,客户端发起一个HTTP请求到服务器上指定端口(默认端口为80)。我们称这个客户端为用户代理程序(user agent)。应答的服务器上存储着一些资源,比如HTML文件和图像。我们称这个应答服务器为源服务器(origin server)。在用户代理和源服务器中间可能存在多个“中间层”,比如代理服务器、网关或者隧道(tunnel)。
安装requests
pip install requests
案例1:
import requests
query = input("输入一个搜索关键字")
url = f'https://www.sogou.com/web?query={query}'
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.82 Safari/537.36"
}
resp = requests.get(url,headers=headers)
print(resp)
print(resp.text)
resp.close()#关闭resp
案例2:
import requests
query = input("输入一个翻译关键字")
url = f'https://fanyi.baidu.com/sug'
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.82 Safari/537.36"
}
data = {
"kw":query
}
# 发送post请求,发送的数据必须放在字典中,通过data参数进行传递。
resp = requests.post(url,data=data)
print(resp)
print(resp.json())
#将服务器返回的内容直接处理成json() => dict
resp.close()#关闭resp
设置--->keys--->Surround selection on typing quote or brace
选择直接加双引号!
案例3:
import requests
url = 'https://movie.douban.com/j/chart/top_list'
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.82 Safari/537.36"
}
#重新封装参数
params = {
"type": "24",
"interval_id": "100:90",
"action": "",
"start": 0,
"limit": 20,
}
resp = requests.get(url,params=params,headers=headers)
# resp.request.url和我们直接在链接后面加是一样的!
# resp.request.headers可以看到当前链接的头是什么
# print(resp.request.headers)
print(resp.json())
resp.close()#关闭resp
一定要记得:resp.close()#关闭resp
否则请求过多,请求(堵塞)失败!
文件也记得关闭!
三种解析方式:
正则表达式
Regular Expression
优点: 速度快,效率高,准确度高
元字符 含义
. 匹配除换行符以外的任意一个字符
^ 匹配行首
$ 匹配行尾
? 重复匹配0次或1次
* 重复匹配0次或更多次
+ 重复匹配1次或更多次
{n,} 重复n次或更多次
{n,m} 重复n~m次
[a-z] 任意字符
[abc] a/b/c中的任意一个字符
{n} 重复n次
\
将下一个字符标记为一个特殊字符、或一个原义字符、或一个 向后引用、或一个八进制转义符。例如,'n' 匹配字符 "n"。'\n' 匹配一个换行符。序列 '\\' 匹配 "\" 而 "\(" 则匹配 "("。
^
匹配输入字符串的开始位置。如果设置了 RegExp 对象的 Multiline 属性,^ 也匹配 '\n' 或 '\r' 之后的位置。
$
匹配输入字符串的结束位置。如果设置了RegExp 对象的 Multiline 属性,$ 也匹配 '\n' 或 '\r' 之前的位置。
*
匹配前面的子表达式零次或多次。例如,zo* 能匹配 "z" 以及 "zoo"。* 等价于{0,}。
+
匹配前面的子表达式一次或多次。例如,'zo+' 能匹配 "zo" 以及 "zoo",但不能匹配 "z"。+ 等价于 {1,}。
?
匹配前面的子表达式零次或一次。例如,"do(es)?" 可以匹配 "do" 或 "does" 。? 等价于 {0,1}。
{n}
n 是一个非负整数。匹配确定的 n 次。例如,'o{2}' 不能匹配 "Bob" 中的 'o',但是能匹配 "food" 中的两个 o。
{n,}
n 是一个非负整数。至少匹配n 次。例如,'o{2,}' 不能匹配 "Bob" 中的 'o',但能匹配 "foooood" 中的所有 o。'o{1,}' 等价于 'o+'。'o{0,}' 则等价于 'o*'。
{n,m}
m 和 n 均为非负整数,其中n <= m。最少匹配 n 次且最多匹配 m 次。例如,"o{1,3}" 将匹配 "fooooood" 中的前三个 o。'o{0,1}' 等价于 'o?'。请注意在逗号和两个数之间不能有空格。
?
当该字符紧跟在任何一个其他限制符 (*, +, ?, {n}, {n,}, {n,m}) 后面时,匹配模式是非贪婪的。非贪婪模式尽可能少的匹配所搜索的字符串,而默认的贪婪模式则尽可能多的匹配所搜索的字符串。例如,对于字符串 "oooo",'o+?' 将匹配单个 "o",而 'o+' 将匹配所有 'o'。
.
匹配除换行符(\n、\r)之外的任何单个字符。要匹配包括 '\n' 在内的任何字符,请使用像"(.|\n)"的模式。
(pattern)
匹配 pattern 并获取这一匹配。所获取的匹配可以从产生的 Matches 集合得到,在VBScript 中使用 SubMatches 集合,在JScript 中则使用 $0…$9 属性。要匹配圆括号字符,请使用 '\(' 或 '\)'。
(?:pattern)
匹配 pattern 但不获取匹配结果,也就是说这是一个非获取匹配,不进行存储供以后使用。这在使用 "或" 字符 (|) 来组合一个模式的各个部分是很有用。例如, 'industr(?:y|ies) 就是一个比 'industry|industries' 更简略的表达式。
(?=pattern)
正向肯定预查(look ahead positive assert),在任何匹配pattern的字符串开始处匹配查找字符串。这是一个非获取匹配,也就是说,该匹配不需要获取供以后使用。例如,"Windows(?=95|98|NT|2000)"能匹配"Windows2000"中的"Windows",但不能匹配"Windows3.1"中的"Windows"。预查不消耗字符,也就是说,在一个匹配发生后,在最后一次匹配之后立即开始下一次匹配的搜索,而不是从包含预查的字符之后开始。
(?!pattern)
正向否定预查(negative assert),在任何不匹配pattern的字符串开始处匹配查找字符串。这是一个非获取匹配,也就是说,该匹配不需要获取供以后使用。例如"Windows(?!95|98|NT|2000)"能匹配"Windows3.1"中的"Windows",但不能匹配"Windows2000"中的"Windows"。预查不消耗字符,也就是说,在一个匹配发生后,在最后一次匹配之后立即开始下一次匹配的搜索,而不是从包含预查的字符之后开始。
(?<=pattern) 反向(look behind)肯定预查,与正向肯定预查类似,只是方向相反。例如,"(?<=95|98|NT|2000)Windows"能匹配"2000Windows"中的"Windows",但不能匹配"3.1Windows"中的"Windows"。
(?
*?
尽可能少的让*
匹配
贪婪匹配和惰性匹配
.* 贪婪匹配
.*? 惰性匹配
findall
import re
#findall:匹配字符串中所有符合正则的内容
lst = re.findall(r"\d+","我的电话号是:10086,她的电话号是:1001")
print(lst)
[‘10086’, ‘1001’]
finditer
import re
#finditer:匹配字符串中所有内容[返回迭代器]
it = re.finditer(r"\d+","我的电话号是:10086,她的电话号是:1001")
for i in it:
print(i.group())
10086
1001
# search返回的是match对象,拿数据需要.group(),找到一个结果就返回!
s = re.search(r"\d+","我的电话号是:10086,她的电话号是:1001")
print(s.group())
# 从头开始匹配
s = re.match(r"\d+","我的电话号是:10086,她的电话号是:1001")
print(s.group())
AttributeError: 'NoneType' object has no attribute 'group'
预加载正则表达式
# 预加载正则表达式
obj = re.compile(r"\d+")
ret = obj.finditer("我的电话号是:10086,她的电话号是:1001")
for i in ret:
print(i.group())
lst = re.findall(r"\d+","千王之王2000")
print(lst)
s = """
AA
BB
CC
DD
EE
"""
obj = re.compile(r"(?P.*?) ",re.S)
# re.S:让.能匹配换行符
result = obj.finditer(s)
for it in result:
print(it.group("id"))
print(it.group("want"))
# (?P<分组名字>正则)可以单独从正则匹配的内容中进一步提取内容
bs4 基本使用
pip install bs4 -i 清华源
拿到页面源代码
使用bs4进行解析,拿到数据!
案例—>北京新发地
import requests
url = "http://www.xinfadi.com.cn/getPriceData.html"
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.82 Safari/537.36"
}
resp = requests.post(url,headers=headers)
print(resp.text)
resp.close()
更换了渲染方式。换其它例子使用bs4
案例1:生猪价格https://zhujia.zhuwang.cc/
import requests
from bs4 import BeautifulSoup
import csv
url = "https://zhujia.zhuwang.cc/"
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.82 Safari/537.36"
}
resp = requests.get(url,headers=headers)
f = open("猪价.csv",mode="w")
csvwriter = csv.writer(f)
# 解析数据
#1 把页面原代码交给bs进行处理。生成bs对象
page = BeautifulSoup(resp.text,"html.parser")#指定html解析器
#2 从bs对象中查找数据
# find#就找第一个和find_all#查找所有
# find(标签,属性=值)
# find_all(标签,属性=值)
#div = page.find("div",class_="bookname") #class是python的关键字
div = page.find("div",attrs={"class":"relevant-areas-detail"})#和上一句语义相同
# print(div)
#拿到所有的tr,做切片
trs = div.find_all("tr")[1:]
# print(trs)
for tr in trs: #每一行
tds = tr.find_all("td")#每一行所有的td
text1 = tds[0].text #.text拿到被标签标记的内容
text2 = tds[1].text #.text拿到被标签标记的内容
text3 = tds[2].text #.text拿到被标签标记的内容
text4 = tds[3].text #.text拿到被标签标记的内容
text5 = tds[4].text #.text拿到被标签标记的内容
text6 = tds[5].text #.text拿到被标签标记的内容
# print(text1,text2,text3,text4,text5,text6)
csvwriter.writerow([text1,text2,text3,text4,text5,text6])
print("over")
f.close()
resp.close()
案例2:唯美壁纸
# 拿到主页面的源代码。然后提取子页面的链接地址,href
# 通过href拿到子页面的内容,拿到图片的地址。
# 下载图片
import requests
from bs4 import BeautifulSoup
import time
url = "https://www.umeitu.com/bizhitupian/weimeibizhi/"
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.82 Safari/537.36"
}
resp = requests.get(url,headers=headers);
resp.encoding = 'utf-8'#处理乱码
#把源代码交给bs
main_page = BeautifulSoup(resp.text,"html.parser")#指定html解析器
alist = main_page.find("div",class_="TypeList").find_all("a")
#https://www.umeitu.com/
for a in alist:
href = a.get('href')#直接通过get可以拿到属性的值
href = "https://www.umeitu.com/"+href
#拿到子页面的源代码
child_page_resp = requests.get(href,headers=headers)
child_page_resp.encoding='utf-8'
child_page_text = child_page_resp.text
#从子页面中拿到图片的下载路径
child_page = BeautifulSoup(child_page_text,"html.parser")
div = child_page.find("div",class_="ImageBody")
img = div.find("img")
src = img.get("src")
# 下载图片
img_resp = requests.get(src,headers=headers)
img_resp.content #这里拿到的是字节
img_name = src.split("/")[-1] #拿到url中最后一个/以后的内容
with open("img/"+img_name,mode="wb") as f:
f.write(img_resp.content) #图片内容写入文件
print("over!!!",img_name)
time.sleep(1)
print("all_over")
resp.close();
# PyCharm会对文件加索引,文件越多,PyCharm越卡,所以我们将文件夹标记为Exluded
# 就不会再对该文件内的文件处理了。
xpath是在XML文档中搜索内容的一门语言
html是xml的一个子集
安装lxml模块
pip install lxml -i xxx
案例1: xml
from lxml import etree
# etree.XML().xpath()
xml = """
1
野花满地香
1.23
香菜
周杰伦
周润发
汤姆
杰瑞
mygold
mygold2
mygold3
胖陈
胖不陈
"""
tree = etree.XML(xml)
result = tree.xpath("/book")
# /表示层级关系,第一个/是根节点
# result = tree.xpath("/book/name")
result = tree.xpath("/book/name/text()")
#text()拿文本
#
# result = tree.xpath("/book/author/nick/text()")
#
# result = tree.xpath("/book/author/div/nick/text()")
# result = tree.xpath("/book/author//nick/text()")#后代
# result = tree.xpath("/book/author/*/nick/text()")#任意节点,通配符
result = tree.xpath("/book//nick/text()")
print(result)
案例2: html
DOCTYPE html>
<html>
<head>
<title>这个是标题title>
head>
<body>
<h1>这是一个一个简单的HTML,h1
<p>Hello World!p>
h1>
<ul>
<li>
<a href="dapao">大炮a>
<a href="feiji">飞机a>
<a href="zhuangjiache">装甲车a>
li>
<li>
<a href="dapao1">大炮1a>
<a href="feiji1">飞机1a>
<a href="zhuangjiache1">装甲车1a>
li>
<li>
<a href="dapao2">大炮2a>
<a href="feiji2">飞机2a>
<a href="zhuangjiache2">装甲车2a>
li>
ul>
<h2>这是一个一个简单的HTML,h2h2>
<h3>这是一个一个简单的HTML,h3h3>
<h4>这是一个一个简单的HTML,h4h4>
<h5>这是一个一个简单的HTML,h5
<p>Hello World!1p>
<p>Hello World!2p>
<h6>这是一个一个简单的HTML3,h6h6>
<p>Hello World!4p>
<p>Hello World!5p>
h5>
body>
html>
from lxml import etree
tree = etree.parse("one.html")
# result = tree.xpath("/html")
# result = tree.xpath("/html/body/h5/p/text()")
# result = tree.xpath("/html/body/h5/p[1]/text()")
# xpath的顺序是从1开始数的
# []表示索引
result = tree.xpath("/html/body/ul/li/a[@href='dapao']/text()")
#[@xxx='zzz'] 属性的筛选
ul_li_list = tree.xpath("/html/body/ul/li")
for li in ul_li_list:
result = li.xpath("./a/text()") #在li中继续去查找。相对查找
# print(result)
result2 = li.xpath("./a/@href")
# print(result2)
#拿到属性值:@属性值
print(tree.xpath("/html/body/ul/li/a/@href"))
# 可以在浏览器选择元素,直接右键复制xpath
登录—>得到cookie
带着cookie去请求url得到内容!
必须连续操作!
可以使用session进行请求->session可以是一连串请求,这个过程cookie不会消失!
import requests
# 会话
session = requests.session()
# A->B
# B->A
# A->B
# 登录
url = "..."
data = {
"loginname" : "xxx",
"password":"xxx"
}
resp = session.post(url,data=data)
print(resp.cookies)
# 拿数据
# 刚才那个session中是有cookie的
resp = session.get("...")
# 另一种麻烦的方式!
resp = requests.get("...",headers={
"Cookie":"... ..."
})
print(resp.text)
resp.close()
案例:梨视频
import requests
#拉取视频的网址
url = "https://www.pearvideo.com/video_1748764"
contId = url.split("_")[1]
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.82 Safari/537.36",
"Referer": "https://www.pearvideo.com/video_1748764"
}
# 溯源,防盗链。能找到上一个网页,能找到当前请求的上一级是谁
videoStatusUrl = f"https://www.pearvideo.com/videoStatus.jsp?contId={contId}&mrd=0.8411394340102853"
resp = requests.get(videoStatusUrl,headers=headers)
dic = resp.json()
srcUrl = dic['videoInfo']['videos']['srcUrl']
systemTime = dic['systemTime']
# 真实视频链接
# https://video.pearvideo.com/mp4/short/20220211/cont-1748764-15825042-hd.mp4
# 得到的视频链接
# https://video.pearvideo.com/mp4/short/20220211/1644843206475-15825042-hd.mp4
srcUrl = srcUrl.replace(systemTime,f"cont-{contId}")
print(srcUrl)
#https://video.pearvideo.com/mp4/short/20220211/cont-1748764-15825042-hd.mp4
# 下载视频
with open("a.mp4",mode="wb") as f:
f.write(requests.get(srcUrl).content)
resp.close()
短时间获得大批量数据!
(管理者封ip)
原理:
通过第三方的机器去发送请求
import requests
# 找个免费代理ip网站
# 223.96.90.216:8085
proxies = {
# "http":"",
# "https":"000.00.0.00:0000",#旧版本写法
"http":"http://223.96.90.216:8085"#新版写法
}
resp = requests.get("https://www.baidu.com",proxies=proxies)
resp.encoding = 'utf-8'
print(resp.text)
resp.close()
异步
多线程
多进程
协程
进程是资源单位,每一个进程至少要有一个线程
线程是执行单位,
启动每一个程序默认都会有一个主线程
第一种写法:
from threading import Thread
def func():
for i in range(1000):
print("func",i)
if __name__ == '__main__':
# 创建线程并给线程安排任务
t = Thread(target=func)
# 多线程状态为可以开始工作状态,具体执行时间由cpu决定
t.start();
# t2 = Thread(...)
for i in range(1000):
print("main",i)
第二种写法:
from threading import Thread
class MyThread(Thread):
def run(self):#固定的
pass
if __name__ == '__main__':
pass
案例:
from threading import Thread
class MyThread(Thread):
def run(self):#固定的 ->当线程被执行的时候,被执行的就是run()
for i in range(1000):
print("子线程",i)
if __name__ == '__main__':
t = MyThread()
#t.run() 是方法的调用-->单线程
t.start()#开启线程
for i in range(1000):
print("主线程", i)
传参
from threading import Thread
def func():
for i in range(1000):
print("func",i)
if __name__ == '__main__':
t1 = Thread(target=func,args=("周杰伦",))
#传递参数必须是元组
t1.start();
t2 = Thread(target=func,args=("jay",))
t2.start();
# t2 = Thread(...)
for i in range(1000):
print("main",i)
传参2
from threading import Thread
class MyThread(Thread):
def run(self):#固定的 ->当线程被执行的时候,被执行的就是run()
for i in range(1000):
print("子线程",i)
if __name__ == '__main__':
def __init__(self):#构造函数传参
t = MyThread()
#t.run() 是方法的调用-->单线程
t.start()#开启线程
for i in range(1000):
print("主线程", i)
from multiprocessing import Process
def func():
for i in range(1000):
print("子进程",i)
if __name__ == '__main__':
p = Process(target=func)
p.start()
for i in range(1000):
print("主进程",i)
第二种写法与多线程相似
线程池:一次性开辟一些线程,用户直接给线程池提交任务
线程任务的调度交给线程池来完成
from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor
def fn(name):
for i in range(1000):
print(name,i)
if __name__ == '__main__':
#创建线程池
with ThreadPoolExecutor(50) as t:
for i in range(100):
t.submit(fn,name=f"线程{i}")
# 等待线程池中的任务全部执行完毕,才继续执行(守护)
print("123")
import time
def func():
print("我爱黎明")
time.sleep(3) #让当前线程处于阻塞状态,cpu不为我工作
#input() 程序也是处于阻塞状态
# request.get() 返回数据之前,程序也是处于阻塞状态
# 一般情况下,当程序处于io操作时,线程会处于阻塞状态
print("我爱黎明2")
if __name__ == '__main__':
func()
print("123")
# 协程 :当程序遇见了io操作的时候,可以选择性的切换到其它任务上
# 一个任务一个任务进行切换,切换条件一般就是io操作
# 宏观上多个任务一起执行
# 多任务异步操作
# 单线程条件下!
# python写协程程序
import asyncio
async def func():
print("你好,我是匿蝶")
if __name__ =='__main__':
g = func()
#此时函数是异步协程函数,此时函数执行得到的是一个协程对象
asyncio.run(g)
# python写协程程序
import asyncio
import time
async def func1():
print("你好,我是匿蝶")
# time.sleep(3) #当程序出现了同步操作(requests.get()也是)的时候,异步就中断了。
await asyncio.sleep(3) #异步操作的代码
print("你好,我是匿蝶")
async def func2():
print("你好,我是cao")
await asyncio.sleep(2)
print("你好,我是cao")
async def func3():
print("你好,我是fuck")
await asyncio.sleep(4)
print("你好,我是fuck")
if __name__ =='__main__':
f1 = func1()
f2 = func2()
f3 = func3()
tasks = [
f1,f2,f3
]
t1 = time.time()
#协程一次性启动多个任务
asyncio.run(asyncio.wait(tasks))
t2 = time.time()
print(t2-t1)
# python写协程程序
import asyncio
import time
async def func1():
print("你好,我是匿蝶")
# time.sleep(3) #当程序出现了同步操作(requests.get()也是)的时候,异步就中断了。
await asyncio.sleep(3) #异步操作的代码
print("你好,我是匿蝶")
async def func2():
print("你好,我是cao")
await asyncio.sleep(2)
print("你好,我是cao")
async def func3():
print("你好,我是fuck")
await asyncio.sleep(4)
print("你好,我是fuck")
async def main():
# 第一种写法:不推荐
# f1 = func1()
# await f1
# await写在async里
# 一般await挂起操作放在协程对象前面
# 第二种写法:推荐
tasks = [
func1(), func2(), func3(),
]
# await asyncio.wait(tasks)
#版本问题 改为:
await asyncio.gather(*tasks)
if __name__ =='__main__':
t1 = time.time()
asyncio.run(main())
t2 = time.time()
print(t2-t1)
协程爬虫模板
# python写协程爬虫
import asyncio
async def download(url):
print("开始下载")
await asyncio.sleep(3) #异步操作的代码
print("下载完成")
async def main():
urls = [
"baidu.com",
"bilibili.com",
"163.com",
]
tasks = []
for url in urls:
d = asyncio.create_task(download(url))
tasks.append(d)
# tasks = [
# asyncio.create_task(download(url)) for url in urls
#]
await asyncio.wait(tasks)
if __name__ =='__main__':
asyncio.run(main())
wait提示3.11抛弃
修改后:
tasks = [
asyncio.create_task(func1()),
asyncio.create_task(func2()),
asyncio.create_task(func3()),
]
浅试一下三张图片:
# request.get() 同步的代码--->改为异步操作aiohttp
# pip install aiohttp
import asyncio
import aiohttp
urls = [
"http://kr.shanghai-jiuxin.com/file/mm/20211130/fqgilkjrjch.jpg",
"http://kr.shanghai-jiuxin.com/file/mm/20211130/vs55vb4b0d1.jpg",
"http://kr.shanghai-jiuxin.com/file/mm/20211130/izk1sjb1pbw.jpg"
]
async def aiodown(url):
# 发送请求 得到图片 保存文件
# aiohttp.ClientSession() <==> requests
# 加了with后 会自动帮忙关闭session
name = url.rsplit("/",1)[1]
# 从右边切,切一次,得到[1]位置的内容
async with aiohttp.ClientSession() as session:
async with session.get(url) as resp:
# resp.content.read() ===> resp.content
# resp.text() ===> resp.text
# resp.json() ===> resp.json()
#自己学习一个模块叫aiofiles
with open(name,mode="wb") as f:
f.write(await resp.content.read())
#读取内容是异步的,需要await
print("写入完成")
async def main():
tasks = []
for url in urls:
tasks.append(asyncio.create_task(aiodown(url)))
await asyncio.wait(tasks)
if __name__ == '__main__':
loop = asyncio.get_event_loop() # 可以防止报错
loop.run_until_complete(main())
搭建环境:
# 能不能让我们的程序连接到浏览器,让浏览器来完成各种复杂的操作,我们只接受最终的结果!
# selenium: 自动化测试工具
# 可以打开浏览器,像人一样操作浏览器
# 我们可以从selenium提取各种信息。
# 怎么用?
# 环境搭建!
# pip install selenium -i 清华源
# 下载浏览器(谷歌浏览器为例)驱动:
# https://registry.npmmirror.com/binary.html?path=chromedriver/
# https://npmmirror.com/package/chromedriver
#把解压缩的浏览器驱动放在python解释器所在的文件夹(运行一下程序会自动显示目录)。
# 让selenium启动谷歌浏览器
from selenium.webdriver import Chrome
# 1、创建浏览器对象
web = Chrome()
# 2、打开一个浏览器网站
web.get("http://www.baidu.com")
# 弹出浏览器并显示Chrome正受到自动测试软件的控制。
print(web.title)
案例: 拉勾网
from selenium.webdriver import Chrome
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
import time
web = Chrome()
web.get("http://www.lagou.com")
# 找到某个元素,点击它
# el = web.find_element_by_xpath('//*[@id="changeCityBox"]/p[1]/a')
# 上面方法过时,我们要添加from selenium.webdriver.common.by import By并且更改语句
el = web.find_element(By.XPATH,'//*[@id="changeCityBox"]/p[1]/a')
el.click() #点击事件
time.sleep(1)
# 让浏览器缓一会,把东西加载完了,再输入(否则可能报错,未加载完)
# 找到输入框,输入 =>输入回车/点击搜索按钮
web.find_element(By.XPATH,'//*[@id="search_input"]').send_keys("python",Keys.ENTER)
time.sleep(1)
# 查找存放数据的位置,进行数据提取
# 找到页面中存放数据的所有的div
div_list = web.find_elements(By.CLASS_NAME,value='item__10RTO')
for div in div_list:
# a = web.find_element_by_tag_name("a")方法过期
a = div.find_element(by=By.TAG_NAME, value='a').text
span = div.find_element(By.CLASS_NAME,value="money__3Lkgq").text
com = div.find_element(By.CLASS_NAME,value='company-name__2-SjF')
c = com.find_element(By.TAG_NAME,value='a').text
print("岗位:"+a+" 薪资:"+span+" 公司:"+c)
案例: 拉勾网窗口切换
from selenium.webdriver import Chrome
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
import time
web = Chrome()
web.get("http://www.lagou.com")
web.find_element(By.XPATH,value='//*[@id="cboxClose"]').click()
time.sleep(1)
web.find_element(By.XPATH,'//*[@id="search_input"]').send_keys("python",Keys.ENTER)
web.find_element(By.XPATH,value='//*[@id="jobList"]/div[1]/div[1]/div[1]/div[1]/div[1]/a').click()
# 如何进入到新窗口进行提取
# 注意:在selenium的眼中,新窗口默认是不切换过来的。
web.switch_to.window(web.window_handles[-1])#window_handles对应选项卡
#在新窗口中提取内容
job = web.find_element(By.XPATH,value='//*[@id="job_detail"]/dd[2]').text
print(job)
# 关掉子窗口,变更selenium窗口视角,回到原来的窗口中!
web.close()
web.switch_to.window(web.window_handles[0])#window_handles对应选项卡
print(web.find_element(By.XPATH,value='//*[@id="jobList"]/div[1]/div[1]/div[1]/div[1]/div[1]/a').text)
还有一种切换!
如果页面中遇到了iframe
先定位到iframe再切换视角到iframe再然后再拿数据
from selenium.webdriver import Chrome
from selenium.webdriver.common.by import By
import time
web = Chrome()
web.get("https://91kanju.com/vod-play/541-1-1.html")
iframe = web.find_element(By.XPATH,value='//*[@id="player_iframe"]')
time.sleep(1)
web.switch_to.frame(iframe)
# 切入切换iframe
text = web.find_element(By.XPATH,value='/html/head/title').text
# web.switch_to.default_content() 切回原来的视角
print(text)
不弹出浏览器。后台跑浏览器
from selenium.webdriver.chrome.options import Options
# 准备好参数
opt = Options()
opt.add_argument("--headless")
opt.add_argument("--disable-gpu")
#把参数配置设置到浏览器中
web = Chrome(options=opt)#,chrome_options=opt已过时,可以不用加
案例:
from selenium.webdriver import Chrome
from selenium.webdriver.common.by import By
from selenium.webdriver.support.select import Select
from selenium.webdriver.chrome.options import Options
import time
# 准备好参数
opt = Options()
opt.add_argument("--headless")
opt.add_argument("--disable-gpu")
#把参数配置设置到浏览器中
web = Chrome(options=opt)#,chrome_options=opt已过时,可以不用加
web.get("https://tool.chinaz.com/")
# 定位到下拉列表
sel_el = web.find_element(By.XPATH,'//*[@id="selDate"]')
# 对元素进行包装,包装成下拉菜单
sel = Select(sel_el)
# 让浏览器调整选项
for i in range(len(sel.options)):#i就是每一个下拉框选项的索引位置
sel.select_by_index(i)# 按照索引进行切换
time.sleep(2)
table = web.find_element(By.XPATH,'//*[@id="rank_up_tab"]')
print(table.text)#打印所有文本信息
print("===========================================")
print("运行完毕.")
web.close()
怎么获得页面代码?
# 怎么获得页面代码Elements?(经过数据加载以及js执行之后的结果的html内容)
print(web.page_source)
很难
选择互联网上成熟的验证码破解工具
超级鹰
注册
生成软件id
开发文档===>python
下载===>chaojiying.py
在mian里改就行了。
im = open('a.jpg','rb').read()
im就是图片的所有字节(图片)
chaojiying.PostPic(im,1902)
把1902改成自己的要破解的验证码的类型(类型在超级鹰官网看)。
返回的pic_str是我们的验证码字符串
获取图片可以使用(直接是字节)
img = we.find_element(...).screenshot_as_png
写入后点击登录即登录。
引入本文件夹的py文件把文件夹标记为Sources Root
即可。
from selenium.webdriver.common.action_chains import ActionChains
# 移动着某个节点,带着偏移量
ActionChains(web).move_to_element_with_offset(...,x,y).click().perform()
ActionChains(web).
btn = web.find_element(By.XPATH,'...')
# 那个span按钮
ActionChains(web).drag_and_drop_by_offset(btn,300,0).perform()#提交事件
执行过程中不要切,可能会导致失败
如果没有找到元素,可能是没有加载就在获取。让它多sleep几秒。
chrome的版本小于88
在启动浏览器的时候,此时没有加载任何网页内容。向页面嵌入js代码,去掉webdriver
web = Chrome() # 创建浏览器对象
# 先嵌入js代码去掉webdriver
web.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument", {
"source": """
window.navigator.webdriver = undefined
Object.defineProperty(navigator, 'webdriver', {
get: () => undefined
})
"""
})
# 之后便可以正常执行操作
web.get(xxxxxx)
大于等于88
引入option
opt = Options() # 创建配置对象
opt.add_argument("--disable-blink-features=AutomationControlled") # 添加配置参数
web = Chrome(options=opt) # 在创建浏览器对象的时候加入配置参数
# 之后便可以正常执行操作
web.get(xxxxx)
还有其它十余种识别selenium的方式。
例如淘宝就非常难爬。