Python爬虫学习笔记

文章目录

  • 爬虫
    • 爬虫在使用场景中的分类
    • 字符集问题
    • 第一个爬虫
    • Web请求全过程
    • Http协议
    • Requests入门
    • 数据解析
      • re模块
    • bs4解析
    • xpath
    • requests进阶概述
      • 处理cookie
      • 防盗链
    • 代理
  • 爬虫提速
    • 多线程
    • 多进程
    • 线程池和进程池
    • 协程
    • aiohttp
  • selenium
    • selenium操作
      • 1、抓取数据
      • 2、窗口切换
      • 3、无头浏览器
    • 验证码
      • 图像识别!
      • 验证码破解工具
  • 程序被识别到了怎么办?

爬虫

爬虫在使用场景中的分类

  • 通用爬虫

    抓取网站系统重要组成部分,抓取的是一整张页面数据。

  • 聚焦爬虫

    建立在通用爬虫的基础之上,抓取页面中特定的局部内容。

  • 增量式爬虫

    检测网站中数据更新的情况,只会抓取网站中最新更新出来的数据。


字符集问题

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

Web请求全过程

服务器渲染:在服务器那边直接把数据和html整合在一起,统一返回给浏览器

客户端渲染:

第一次请求只要一个html骨架,第二次请求拿到数据,进行数据展示。

在页面源代码中,看不到数据。

熟练浏览器抓包工具!

Http协议

HTTP是一个客户端终端(用户)和服务器端(网站)请求和应答的标准(TCP)。通过使用网页浏览器、网络爬虫或者其它的工具,客户端发起一个HTTP请求到服务器上指定端口(默认端口为80)。我们称这个客户端为用户代理程序(user agent)。应答的服务器上存储着一些资源,比如HTML文件和图像。我们称这个应答服务器为源服务器(origin server)。在用户代理和源服务器中间可能存在多个“中间层”,比如代理服务器、网关或者隧道(tunnel)。

Requests入门

安装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

否则请求过多,请求(堵塞)失败!

文件也记得关闭!

数据解析

三种解析方式:

  • re解析
  • bs4解析
  • xpath解析

正则表达式

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"。
(?

*?尽可能少的让*匹配

贪婪匹配和惰性匹配

.* 贪婪匹配
.*? 惰性匹配

re模块

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解析

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

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


requests进阶概述

处理cookie

登录—>得到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()),
]

aiohttp

浅试一下三张图片:

# 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: 自动化测试工具
# 可以打开浏览器,像人一样操作浏览器
# 我们可以从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)

selenium操作

1、抓取数据

案例: 拉勾网

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)

2、窗口切换

案例: 拉勾网窗口切换

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)

3、无头浏览器

不弹出浏览器。后台跑浏览器

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

Python爬虫学习笔记_第1张图片

写入后点击登录即登录。

引入本文件夹的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

Python爬虫学习笔记_第2张图片

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的方式。

例如淘宝就非常难爬。

你可能感兴趣的:(Python,python)