爬虫教程

需要准备的东西:

python基础, html, css

web请求过程分析

  1. 服务器的渲染:在服务器那边直接把数据和html整合在一起,统一返回给浏览器
    • 在页面源代码中可以看到数据
  2. 客户端渲染:
    • 第一次请求只要一个html骨架,第二次请求拿到数据,进行数据展示
    • 在页面源代码中看不到数据

HTTP协议(超文本传输协议)

Http协议吧一条信息分为三大块内容,无论是请求还是相应都是三大块

请求:

1. 请求行 -> 请求方式	请求url地址	协议
2. 请求头 -> 放一些服务器要使用的附加信息
3. 请求体 -> 一般放一些请求参数

相应

1. 状态行 -> 协议 状态码
2. 响应头 -> 放一些客户端要使用的一些附加信息
3. 响应体 -> 服务器返回的真正客户端要使用的内容(Html, json)等

请求头中最常见的一些重要内容(爬虫需要):

  1. User-Agent:请求载体的身份标识(用啥发送的请求)
  2. Referer:防盗链(这次请求是从哪个页面来的,反爬会用到)
  3. cookie:本地字符串数据信息(用户登录信息,反爬的token)

响应头中一些重要的内容

  1. cookie:本地字符串数据信息(用户登录信息,反爬的token)
  2. 各种神奇的莫名存在的字符串(这个需要经验,一般都是token,放着各种攻击和反爬)

请求方式:

GET:显示提交

POST:隐式提交

数据解析概述

正则表达式

Regular Expression,正则表达式,一种使用表达式的方式对字符串进行匹配的语法规则

我们抓取到的网页源代码本质上就是一个超长的字符串,想从里面提取内容,则正则再适合不过了

正则优点:速度快,效率高,准确性高

正则的缺点:新手上手难度有点高

不过只要掌握了正则编写的逻辑关系,写出一个提取网页内容的正则其实并不复杂

正则语法:使用元字符进行排列组合用来匹配字符串,可以在正则表达式在线测试

元字符:具有固定含义的特殊符号

常用元字符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
1.	.	匹配除换行符以外的任意字符
2. \w 匹配字母或数字或下划线
3. \s 匹配任意的空白字符
4. \d 匹配数字
5. \n 匹配一个换行符
6. \t 匹配一个换行符
7. ^ 匹配字符串的开始
8. $ 匹配字符串的结尾
9. \W 匹配非字母或数字或下划线
10. \D 匹配非数字
11. \S 匹配非空白字符
12. a|b 匹配字符a或字符b
13. () 匹配括号内的表达式,也表示一个组
14. [...] 匹配字符组中的字符 // [a-zA-Z0-9]匹配a-z和A-Z和0-9之间的字符
15. [^...] 匹配处理除字符数组中字符的所有字符

一般一个元字符表示一个字符

一般 ^和配合使用:^\d\d\d

量词:控制前面的元字符出现的次数

1
2
3
4
5
6
*	重复零次或更多次
+ 重复一次或更多次
? 重复零次或一次
{n} 重复n次
{n,} 重复n次或更多次
{n,m} 重复n到m次

贪婪匹配和惰性匹配

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

re表达式

  1. findall:匹配字符串中所有的复合正则表达式的内容

    • lst = re.findall(r"\d+","我的电话号码是:10086, 我女朋友的电话号码是:10010“)
      • print(list) ====== [‘10086’, ‘10010’] // 使用findall返回的是一个列表
  2. finditer:匹配字符串中所有的内容[返回一个迭代器],从迭代器中获取每个元素使用.group()

    1. it = re.finditer(e"\d+", "我的电话号码是:10086, 我女朋友的电话号码是:10010“)

      1
      2
      for i in it:
      print(i.group)
  3. search,找到一个结果就返回,返回的结果是match对象,从中拿数据需要使用.group

    • s = re.search(r"\d+","我的电话号码是:10086, 我女朋友的电话号码是:10010“)
    • print(s.group) ======= 10086// 只返回一个结果
  4. match 是从头开始匹配

    • s = re.match(r"\d+",“我的电话号码是:10086, 我女朋友的电话号码是:10010”)
    • print(s.group) === 会报错 ,因为s为空
    • s = re.match(r"\d+","10086, 我女朋友的电话号码是:10010“)
    • print(s.group) ==== 10086
  5. 预加载正则表达式(将正则表达式放在变量中)

    1
    2
    3
    4
    5
    obj = re.compile(r"\d+")

    ret = obj.finditer("我的电话号码是:10086, 我女朋友的电话号码是:10010")
    for it in ret:
    print(it.group)
  6. 通过正则表达式获取其中一部分东西

    • 获取方法 :(?P<分组名字>正则表达式) 这样可以单独的从正则匹配的内容中进一步提取内容
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    s = """
    <div class='jay'><span id='1'>郭麒麟</span></div>
    <div class='jj'><span id='2'>宋铁</span></div>
    <div class='jolin'><span id='3'>大聪明</span></div>
    <div class='sylar'><span id='4'>范思哲</span></div>
    <div class='tory'><span id='5'>喜羊羊</span></div>
    """

    # 预加载正则表达式
    obj = re.compile(r"<div class='.*?'><span id='\d'>(?P<name>.*?)</span></div>")
    result = obj.finditer(s)
    for it in result:
    print(it.group("name"))
    // 输出结果
    郭麒麟
    宋铁
    大聪明
    范思哲
    喜羊羊

  7. 当我们在使用requests请求的时候如果报错的话,出现

    exceptions.SSLError:HTTPSConnectionPool 这种情况,一般是requests函数内部检查出错,我们可以使用:

    requests.get(url, verify=Flase) // verify = Flase 去掉安全验证

bs4解析

  1. 把页面源代码交给BeautifulSoup进行处理,生成bs对象

    page = BeautifulSoup(resp.text,“html.parser”)

  2. 从bs对象中查找数据

    1
    2
    3
    4
    5
    6
    7
    find(标签,属性=值)
    find_all(标签,属性=值)
    # 当我们使用属性 = 值的时候属性的名称可能和python中的关键字重名,这样会报错
    # 现在给出一下解决方法
    table = page.find("table", class_="hq_table") # 这里class属性名就和python中的关键字重复了,我们可以加下划线
    # 另一种解决方法
    table = page.fine("table", attrs={"class" : "hq_table"}) # 这种写法和上面的相同
  3. ".text"方法可以获取标签中包裹着的文本

  4. “.get(‘herf’)” 获取标签的属性

xpath解析

xpath 是在XML文档中搜索内容的一门语言

html是XML的一个子集

Xpath寻找方式是从父节点,到子节点这样的方式寻找

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<book>
<id>1</id>
<name>zhang</name>
<author>
<nick>周大强</nick>
<nick>周芷若</nick>
<div>
<nick>阿布</nick>
<nick>安安</nick>
</div>
<span>
<nick>大胖</nick>
</span>
</author>
</book>
# 寻找方式: /book/author/ nick 其中 “/” 表示层级关系,第一个 / 表示从根节点开始寻找 / 表示获取父节点的(儿子)
# /book/author/ nick/text() 这样就获取了nick中的文本了 text()表示获取文本
# 获取author里面的所有nick只通过 / 这个符是不行的 / 符只能一层层寻找不能够跨层寻找
# /book/author//nick/text() 这样就拿到author里面所有的nick了 // 表示获取父节点的后代(子孙)
# /book/author/*/nick/text() 这样就拿到author里面隔一个标签里面的nick了 * 通配符(任意节点)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<ul>
<li><a href="www.baidu.com">百度</a></li>
<li><a href="www.google.com">谷歌</a></li>
<li><a href="www.sougou.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>
1
2
3
4
5
6
7
8
9
10
# result = tree.xpath('/html')
# result = tree.xpath('/html/body/ul/li/a/text()') 寻找ul/li/a标签中所有的文本
# result = tree.xpath('/html/body/ul/li[1]/a/text()') 寻找ul/li中第一个li标签中的a标签 xpath顺序是从1开始的
# 下面这是个寻找ul/li/a标签中href为“dapao”的标签的文本 [@xxx = xxx] 表示属性的筛选
# result = tree.xpath('html/body/ol/li/a/[@href = "dapao"]/text()')
ol_li_list = tree.xpath("html/body/ol/li")
for li in ol_li_list:
# 从每个li中提取到文字信息
result = li.xpath("./a/text()") # 在li中继续寻找 使用 " . " 这个表示当前目录
result2 = li.xpaht('.a/@href') # 拿到属性值@ 属性

注意:如果我们想要通过Xpath得到一个特定标签中的所有内容的话可以使用一下方法,例如我们先要得到一个div标签在网页中的html形式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 我们先通过xpath得到我们先要的div标签
# 如果我们不转换div 标签的话 div 是<Element div at 0x226bd81c380>
div = html.xpath("//div[@class='content-article']")[0]
# 然后通过etree模块, 注意一定是etree模块!!!!, 里面的tostring方法将div 这个对象变成字符串的格式,编码格式是utf-8,然后再通过decode方法 把他反编译过来即可
html = etree.tostring(div,encoding="utf-8").decode("utf-8")
# 反编译之后html变成了
"""
<div class="content-article">
<!--导语-->
<p class="one-p">3月12日凌晨,据“杭州发布”最新通报:杭州新增11例新冠肺炎确诊病例,均在顺丰速运余杭中转场工作。</p>
<p class="one-p">随后,“嘉兴发布”、“湖州发布”分别发布通报,各发现1例阳性病例,均为杭州余杭区仁和顺丰员工。</p>
<p class="one-p">至此,<strong>顺丰速运余杭中转场已有27人确诊。</strong></p>
<p class="one-p"><strong>顺丰速运发布重要公告</strong></p>
<p class="one-p">3月11日,顺丰速运发布公告:</p>
</div>
"""

requests 进阶概述

模拟浏览器登录 —> 处理cookie

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 登录  --> 得到cookie
# 带着cookie 去请求后台页面 --> 获取只有登录才能访问的页面

# 我们需要把上面的步骤连起来,保证cookie不会丢失
# 这里我们就可使用session进行请求 -> session请求你可以认为是一连串的请求,在这个过成功cookie,不会丢失

import requests

data = {
"loginName":""
"password":""
}

session = requsets.session()
# 1. 登录
url = "...."
# 通过session来请求页面,这样的话session就会记住cookie,方便我们下一步访问
resp = session.post(url, data = data)
# print(resp.cookies) # 看cookie

# 2.拿书架上的数据
# resp = requests.get(url = "...") !! 千万不要这样写,因为这样会向浏览器从新发送请求,会丢失cookie访问失败
# 我们使用session 来进行访问:
resp = session.get("....")
print(resp.json())

防盗链处理 —>抓取梨视频数据

防盗链的用途:就是在你访问这个页面之前,你必须访问referer指向的网址

页面源代码和F12中出现的代码可能是不同的,F12中出现的可能是动态加载的

代理 ---->防止别封ip

异步操作

多线程, 多进程

进程是资源单位,每一个进程至少有一个线程,线程是执行单位,在平时的时候,我们尽量使用多进程,少使用线程,线程比较消耗内存

多线程导入方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
def fun():
for i in range(1000):
print("func ",i)
class MyThread(Thread):
def fun():
for i in range(1000):
print("func ",i)

from threading import Thread

if __name__ == "__main__":
# 一种是这样调用Thread()的方式创建多线程方式
t = Thread(func) # 把要进行多线程的函数放进去
t.start() # 命令线程开始运作

#另一种是创建一个类,让这个类继承Thread 这样这个类也就是多线程类了
t = MyThread()
t.start()


for i in range(1000):
print("main ",i)



多进程导入方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from muliprocessing import Process
# 剩下的方式和多线程一样

if __name__ == "__main__":
# 一种是这样调用Process()的方式创建多线程方式
t = Process(func) # 把要进行多线程的函数放进去
t.start() # 命令线程开始运作

#另一种是创建一个类,让这个类继承Thread 这样这个类也就是多线程类了
t = MyProcess()
t.start()


for i in range(1000):
print("main ",i)

线程池,进程池

线程池:一次性开辟一些线程,我们用户直接给线程池交任务,线程任务由线程池调用来完成

线程池调用方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import requests
import csv
from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor

def price(url,page):
data={
'limit': 20,
'current': page
}
resp = requests.post(url=url,data=data)
lits = resp.json().get('list')
with open("Vegetable.csv", "a", encoding="utf-8") as f:
csv_writer = csv.writer(f)
for lit in lits:
csv_writer.writerow(lit.values())


if __name__ == '__main__':
with ThreadPoolExecutor(200) as t: # 这里是开启200个线程池用来同时进行下面这个函数,数据比较多,所以使用多线程
for i in range(21187):
t.submit(price,url="http://www.xinfadi.com.cn/getPriceData.html",page=i)
print("over!")

多进程方法和多项成方法一样

协程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def fun():
print("1")
time.sleep(3) # 让当前线程处于堵塞状态,CPU是不为我工作的
print("2")

# input() 程序也是处于堵塞状态
# requests.get(bilbili) 在网络请求方会数据之前,程序也是处于堵塞状态的
# 一般情况下,当程序处于 IO操作的时候,线程都会处于堵塞状态

# 携程:当程序遇见IO操作的时候,可以选择性的切换到其他任务上
# 在微观上是一个任务一个任务的进行切换,切换条件一般是遇见IO操作了
# 在宏观上我们能看到的是多个任务一起再执行
# 多任务异步操作

# 上方所讲的一切都是在单线程条件下
#

使用python程序编写协程

  1. 例子(没有IO流的时候,协程和没有协程时间相同)
1
2
3
4
5
6
7
8
import asyncio

async def func():
print("Hello World")

if __name__ == "__main__":
g = func() # 此时的函数是异步协程函数,返回值是一个协程对象
asyncio.run(g) # 协程程序运行需要sayncio模块的支持
  1. 存在协程的时候,协程的最长时间是根据函数中停顿的最长时间来确定的

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    import asyncio
    import time


    async def fun1():
    print("I am fun1")
    time.sleep(2)
    print("I am fun1")


    async def fun2():
    print("I am fun2")
    time.sleep(3)
    print("I am fun2")


    async def fun3():
    print("I am fun3")
    time.sleep(4)
    print("I am fun3")


    if __name__ == '__main__':
    f1 = fun1()
    f2 = fun2()
    f3 = fun3()
    fun = [
    f1, f2, f3
    ]
    t1 = time.time()
    asyncio.run(asyncio.wait(fun))
    t2 = time.time()
    print(t2-t1)
    '''
    I am fun3
    I am fun3
    I am fun1
    I am fun1
    I am fun2
    I am fun2
    9.023541688919067
    '''

    由上面代码可知,即便我们使用了async 修饰需要进行协程的函数,以及使用asyncio。run() 配合 ascynio.wait() 来进行多程序的运行,但是结果依旧是 9秒多,说明此时协程并没有起上作用

    主要原因在于:time.sleep(3) 是同步操作,当异步程序出现同步操作的时候,异步就中断了,所以我们在使用异步操作的时候,程序中不能出现同步的代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    import asyncio
    import time


    async def fun1():
    print("I am fun1")
    # time.sleep(2) # 当程序出现同步操作的时候,异步就会终止
    await asyncio.sleep(2) # 异步操作的代码
    print("I am fun1")


    async def fun2():
    print("I am fun2")
    await asyncio.sleep(3)
    print("I am fun2")


    async def fun3():
    print("I am fun3")
    await asyncio.sleep(4)
    print("I am fun3")


    if __name__ == '__main__':
    f1 = fun1()
    f2 = fun2()
    f3 = fun3()
    fun = [
    f1, f2, f3
    ]
    t1 = time.time()
    asyncio.run(asyncio.wait(fun))
    t2 = time.time()
    print(t2 - t1)
    '''
    I am fun3
    I am fun1
    I am fun2
    I am fun1
    I am fun2
    I am fun3
    4.016987562179565
    '''

    由上面的程序所得,该程序运行时间是改程序中持续最长时间的程序所决定

    协程程序运行的几种方法:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    # 第一种
    if __name__ == '__main__':
    f1 = fun1()
    f2 = fun2()
    f3 = fun3()
    fun = [
    f1, f2, f3
    ]
    t1 = time.time()
    asyncio.run(asyncio.wait(fun))
    t2 = time.time()
    print(t2 - t1)
    # 第二种
    async def main():
    # 2.1
    f1 = fun1()
    await f1 # 一般await 挂起操作放在协程对象前面
    f2 = fun2()
    await f2
    f3 = fun3()
    await f3

    # 2.2 推荐
    task = [
    asyncio.create_task(fun1()), # 将写协程对象包装成task对象
    asyncio.create_task(fun2()),
    asyncio.create_task(fun3()),
    ]
    await asyncio.wait(tasks)

    if __name__ == '__main__':
    t1 = time.time()
    asyncio.run(main())
    t2 = time.time()
    print(t2-t1)

    为什么推荐使用2.2,因为2.2可以完美的套用在爬虫上面

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    async def download(url):
    print("Start Download")
    await asyncio.sleep(2) # 网络请求 这里可不能使用request.get()操作,因为这是同步代码,不能够再异步操作中使用
    print("End Download")

    async def main():
    url = [
    "http://www.baidu.com",
    "http://www.bilibili.com",
    "http://www.163.com"
    ]
    tasks = []
    for url in urls:
    d = asyncio.create_task(download(url)) # 这时候download函数并不会进行运作,而是返回一个写成对象
    tasks.append(d)

    await asyncio.wait(tasks)

    if __name__ == '__main__':
    asyncio.run(main())

    异步操作,在requests上面的使用:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    import aiohttp
    import asyncio


    url = [
    "http://browser9.qhimg.com/bdr/__85/t010448c46c1ecf7cab.jpg",
    "http://browser9.qhimg.com/bdr/__85/t016ad88ddaf2ae2d92.jpg",
    "http://browser9.qhimg.com/bdr/__85/t01028e5f2ec69e423d.jpg"
    ]

    async def aindownload(url):
    # 发送图片
    # 得到图片内容
    # 保存到文件
    name = url.resplit("/", 1)[1]
    async with aiohttp.ClientSession() as session: # requests
    async with session.get(url) as resp: # resp.get()
    # 请求回来之后,写入文件
    # 可以自己去学习一个模块,aiofiles
    with open(name, mode = "wb") as f:
    f.write(await resp.content.read()) # 读取内容是异步的,需要await挂起
    print("name", "搞定")
    async def main():
    tasks = []
    for url in urls:
    task.append(asyncio.create_task(download(url)))
    await asyncio.wait(tasks)

    if __name__ == '__main__':
    asyncio.run(main())

selenium(可见即可爬)

可以打开浏览器,像人一样去操作浏览器

然后我们可以直接提取网页中的各种信息

环境搭建:

1
2
3
4
5
pip install selenium

下载浏览器驱动 https://npmmirror.com/mirrors/chromedriver?spm=a2c6h.24755359.0.0.6d446093haLcsz

把解压缩的浏览器驱动chormedriver,放在python解释器所在的文件夹中
1
2
3
from selenium.webdriver import Chrome
web = Chrome()
web.get("https://www.baidu.com")

selenium 处理动态网页的时候有奇效!!!

  1. 查找元素:

    web.find_element_by_xpath(“xpath的路径”)

    如果查找的是一个按钮的话可以使用click() 做点击动作

  2. 输入文字:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    from selenium.webdriver import Chrome
    from selenium.webdriver.common.keys import Keys

    # 创建浏览器
    web = Chrome()
    # 搜索页面
    web.get("https://www.baidu.com")
    # 获取该页面的输入框
    # 这时候可能报错,因为如果页面还灭有加载出来的话就去寻找这个输入框的话,就会报错
    web.find_element_by_xpath("输入框的xpath").send_keys("python",Key.ENTER)
    # 这里面的send_keys是向输入框中输入文字, 其中Key.ENTER 是输入回车(特殊含义的字符)
  3. 获取文字:

    使用xpath定位到文字对应的标签上面,使用.text 获取文字即可、

  4. 窗口之间的切换

    我们在使用xpath点击网址的时候会出现一个新网页

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    web.find_element_by_xpath("a标签的xpath地址")
    # 如何进入到窗口中进行提取:
    # 注意,虽然我们打开了一个新的网页,但是默认的话还是在原来的窗口,新窗口默认是不会切换回来的
    # 切换方法:
    web.switch_to.window(web.window_handles[-1]) # 切换到窗口列表中的最后一个

    #新窗口中提取内容
    content = web.find_element_by_xpath("需要获取文字的下path路径").text

    # 关掉子窗口:
    # 注意,即使关掉子窗口之后,selenium的视角也不会切换回来,需要手动切换回来
    web.close()

    # 切换到原来的窗口
    web.switch_to_window(web.window_handles[0])

    # 如果页面中遇到了iframe:selenium 是看不见iframe的,我们必须进入到iframe中, 然后才能够拿到数据,!!!iframe里面是一个html 文件的
    iframe = web.find_element_by_xpath("iframe 的xpath路径") # 获取到需要切换的哪一个iframe,, 因为一个页面中可能有多个iframe,所以我们要定位到我们需要切换到的哪一个iframe
    # 切换到目的iframe
    web.switch_to.frame(iframe)

    # 切换回原页面:
    web.switch_to.default_content()

  5. 下拉列表:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    from selenium.webdriver import Chrome
    from selenium.webdriver.support.select import Select
    # 先定位到对应的下拉列表:
    sel_el = web.find_element_by_xpath("对应的下拉列表的select标签")
    # 对元素进行包装:
    sel = Select(sel_el)
    # 对下拉列表中的元素进行切换
    for i in range(len(sel.options)): # i 就是每一个下拉框选项的索引位置
    sel.select_by_index(i) # 按照索引进行切换,也可以按照value 和 text
    time.sleep(2) # 每一次切换都需要发送一个请求,所以我们在这等两秒钟
    table = web.find_element_by_xpath("文本所在的xpath路径")
    # 这样就获取到了对应的下拉列表中的数据

  6. 无头浏览器:

    定义:就是我们在使用selenium爬取数据的时候,我们其实不关注浏览器的页面,我们主要关注的是,我们想要的内容,让浏览器在后台运行就行,获取到数据返回给我

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    from selenium.webdriver import Chrome
    from selenium.webdriver.chrome.options import Options

    # 准备好参数配置:
    opt = Options()
    opt.add_argument("--headless")
    opt.add_argument("--disbale-gup")

    # 把参数配置弄到浏览器中,这样就不会显示浏览器界面了,
    web = Chrome(options = opt)

  7. 获取页面代码:(经过数据加载以及js执行之后的html中的内容)

    1
    web.page_source

破解验证码 ---- 超级鹰

  1. 直接找到该网站,注册一下,然后获取一下id即可调用

  2. 我们在获取验证码的时候首先要获取验证码这个图片,然后在进行识别,

    1
    2
    #图片的识别:
    img = web.find_element_by_xpath("验证码所在的img的xpath路径").screenshot_as_png
  3. 当我们遇到需要点击验证的验证码的时候,我们可以先通过超级鹰,解读验证码,获取到对应位置的横纵坐标 然后移动鼠标到该位置进行点击

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    from selenium.webdriver.common.action_chains import ActionChains
    verify_img_element = web.find_element_by_xpath("图片路径")
    # 通过超级鹰网站下载的破解验证码的模块破解验证码,返回值是一个字典
    dic = chaojiying.PostPic(verify_img_element.screenshot_as_png,9004)
    rs_list = result.split("|")
    for re in re_list:
    p_temp = re.split(",")
    x = int(p_temp[0])
    y = int(p_time[1])
    # 要让鼠标移动到某一个位置,然后进行点击
    # 这时候我们需要导入事件链模块:
    # from selenium.webdriver.common.action_chains import ActionChains
    ActionChains(web).move_to_element_with_offset(verify_img_element,x,y).click().perform()
    #上面代码的意思是先移动鼠标到该验证码图片的左上角,然后以左上角为基点,进行偏移,一定要加perform()否则点击指令不会执行

  4. 如果我们遇到能够识别我们是自动化工具在操纵浏览器的时候

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    # Chrome版本号小于88 我们可以在web = Chrome()后面加上:
    web = Chrome()
    web.execute_cdp_cmd("Page,addScriptToRvaluateOnNewDocument", {
    "source":"""
    window.navigator.webdriver = undefined
    Object.defineProperty(navigator,'webdriver',{
    get:() =>undefined
    })
    """
    })
    web.get(xxxxx)
    1
    2
    3
    4
    5
    6
    # Chrome版本号大于88 我们可以通过导入option来进行:
    from selenium.webdriver.chrome.options import Options
    options = Options()
    option.add_argument('--disable-blink-features=AutomationControlled')
    web = Chrome(options = options)
    web.get(xxxx)

5.拖拽按钮:

1
2
3
btn = web.find_element_by_xpath("按钮的xpath路径")
# 这里面的第一个参数是 按钮的属性,第二个参数是横向移动的xp,第三个是纵向移动的xp
ActionChains(web).drag_and_drop_by_offset(btn, 300, 0).perform()