SugarCrash!

记录学习,想找个师傅带带我

0%

爬虫&反爬虫学习初探

《Python 3反爬虫原理与绕过实战》初步学习

开发环境配置

安装steamboat

Steamboat 由以下3个 Docker 镜像组成

1
2
3
docker pullregistry.cn-hangzhou.aliyuncs.com/steamboat/steamboat:1
docker pullregistry.cn-hangzhou.aliyuncs.com/steamboat/steamboat:2
docker pullregistry.cn-hangzhou.aliyuncs.com/steamboat/steamboat:3

启动多个镜像写shell文件

1
2
3
4
5
6
7
8
9
创建runsp.sh

写入
docker run -d -p 80:80 -p8090:8090 -p 8205:8205 -p 8207:8207 9b5cc6bd42d0
docker run -d -p 8202:8202 0487eb7998d9
docker run -d -p 8206:8206 5c75ec9ef2b6

启动
sh runsp.sh

Splash

Splash 是一个异步的 JavaScript 渲染服务。它是带有 HTTP API 的轻量级 Web 浏览器,能够并行处理多个页面请求,可以在页面上下文中执行自定义的 JavaScript 以及模拟浏览器中的点击、下滑等操作。
Splash的安装方式有两种,一种是下载已经封装好的 Docker 镜像,另一种是从 GitHub 下载源码后安装,这里我推荐第一种安装方式。在安装好 Docker 后,只需要从 DockerHub 中拉取 Splash 镜像并运行即可,相关命令如下:

1
docker run -it -p 8050:8050 scrapinghub/splash

image-20221012125718154

Puppeteer

Puppeteer 是谷歌官方出品的一个 Node.js库,提供了一个高级 API来控制DevTools协议上的Chrome 或 Chromium。Puppeteer默认无界面运行,但可以配置为运行有界面的 Chrome 或 Chromium。

在用户浏览器中,使用Puppeteer可以完成大多数手动执行的操作。有开发者开源了支持 Python 的 Puppeteer 库,叫作 Pyppeteer,其文档网址为https://miyakogi.github.io/pyppeteer/ 。要注意的是,它仅支持在 Python 3.6 + 的环境下运行。同样,我们可以使用Python 包管理工具 pip来安装 Pyppeteer,命令如下

1
pip install pyppeteer
验证
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import asyncio
from pyppeteer import launch


async def main():
# 初始化浏览器对象
browser = await launch()
page = await browser.newPage()
# 访问指定url
await page.goto('http://example.com')
# 打开网址后进行截图并保存在当前路径
await page.screenshot({'path': 'example.png'})
# 关闭浏览器对象
await browser.close()


asyncio.get_event_loop().run_until_complete(main())

第一次运行 Pyppeteer 时,它会下载最新版本的Chromium(大小约 100 MB),所以第一次运行的等待时间较长。命令运行完毕后,在 pyteer.py 的同级目录下就会多出名为 example.png的图片,说明 Pyppeteer 安装成功,且可以正常运行。

PyTesseract

安装: https://blog.csdn.net/spatial_coder/article/details/123938274

pytesseract.py的cmd路径
将cmd路径改为本机Tesseract-OCR.exe所在的路径

image-20221012132234386

1
2
3
4
5
6
7
try:
from PIL import Image
except ImportError:
import Image
import pytesseract

print(pytesseract.image_to_string(Image.open('example.png')))

首先导入 Image 库和 PyTesseract 库,接着使用 PyTesseract 库中的image_to_string() 方法识别
example.png 图片中的文字,并打印识别结果。

image-20221012132319191

Parsel

Parsel 库是 Scrapy 开发团队开源的一个支持 XPath和 CSS 选择器语法的网页解析

反爬虫的概念与定义

  • 主动型反爬虫 :开发者有意识地使用技术手段区分正常用户和爬虫,并限制爬虫对网站的访问行为,如验证请求头信息、限制访问频率、使用验证码等。

  • 被动型反爬虫 :为了提升用户体验或节省资源,用一些技术间接提高爬虫访问难度的行为,比如数据分段加载、点击切换标签页、鼠标悬停预览数据等。

User-Agent反爬虫

User-Agent 反爬虫指的是服务器端通过校验请求头中的User-Agent值来区分正常用户和爬虫程序的手段,这是一种较为初级的反爬虫手段。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import requests
from lxml import etree


url = "http://www.porters.vip/verify/uas/index.html"
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Safari/537.36"
}

resp = requests.get(url, headers=headers)
print(resp.status_code)
html = etree.HTML(resp.text)

if resp.status_code == 200:
divs = html.xpath('/html/body/div[2]/div[2]/div')
for a in divs:
biaoti = a.xpath('./div/h4[1]/a[1]/text()')
print(biaoti)
else:
print('This request is fial.')

User-Agent校验流程

利用 nginx 的条件判断语句实现反爬虫。比如将 Python、Java和 PHP 等关键词加入黑名单,那么只要服务器检测到User-Agent头域值中包含黑名单中的关键词时,就会将此次请求的发起者视为爬虫,可以不予处理或者返回相应的错误提示。

image-20221012150930620

示例

在nginx 的辅助配置文件目录下新建一个名为 porters.conf 的辅助配置,将以下配置写入文件:

1
2
3
4
5
6
7
8
9
10
11
12
server {
listen 80;
server_name www.porters.vip; # 请填写真实域名
charset utf-8;
location /verify/uas/ {
if ($http_user_agent ~* (python)){
return 403;
}
root /root/www/html;
index index.html;
}
}

这段配置的作用是判断请求头信息中User-Agent头域值里是否包含关键字python ,如果包含,则直接返回 403 错误。注意,配置中的server_name 必须是域名或 IP 地址,否则无法访问。然后,在
root 指令设定的目录下建立层级目录 /verify/uas。

Cookie反爬虫

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 网址 :http://www.porters.vip/verify/cookie/content.html 。
# 任务 :爬取旅游网公告详情页中的公告标题

import requests
from lxml import etree

url = 'http://www.porters.vip/verify/cookie/content.html'
# 向目标网址发起网络请求
header = {"Cookie": "isfirst=789kq7uc1pp4c"}
resp = requests.get(url, headers=header)
# 打印输出状态码
print(resp.status_code)
# 如果本次请求的状态码为200,则继续,否则提示失败
if resp.status_code == 200:
# 将响应正文赋值给html变量
html = etree.HTML(resp.text)
# 根据HTML标签名称和类名从文档中取出标题
res = html.cssselect('.page-header h1')[0].text
print(res)
else:
print('This request is fial.')

签名验证反爬虫

http://www.porters.vip/verify/sign/

页面加载了两个js文件一个md5加密的js文件,一个sign.js文件,sign.js

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
function fetch(){
text=$.ajax({
type:"GET", async: false,
url:"http://www.porters.vip/verify/sign/fet" + uri()
});
$("#content").html(text.responseText);
}

function randints(r, n, tof){
/* 生成随机数字,tof决定返回number类型或者字符串类型
r 代表数字范围 n 代表数量
*/
var result = [];
if(tof){
return Math.floor(Math.random()*r);
}
for(var i=0;i<n;i++){
s = Math.floor(Math.random()*r);
result.push(s);
}
return result.join('');
}
function randstrs(n){
// 生成随机字母,n为随机字母的数量
var result = [];
for(var i=0; i<n; i++){
s = String.fromCharCode(65+randints(25, 1, 1));
result.push(s);
}
return result.join('');
}
function uri(){
var action = randints(9, 5, 0);
var tim = Math.round(new Date().getTime()/1000).toString();
var randstr = randstrs(5);
var hexs = hex_md5(action+tim+randstr);
args = '?actions=' + action + '&tim=' + tim + '&randstr=' + randstr + '&sign=' + hexs;
return args;
}

直接看最后可以得知,action,tim,randstr,hexs这几个值

action是生成1-9的几个随机数

tim时间戳

randstr是生成5个随机字母代码得知是大写字母

hexs将前面所有的值加起来md5加密

python实现相同代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from time import time
from random import randint, sample
import hashlib


def hex5(value):
manipulator = hashlib.md5()
manipulator.update(value.encode('utf-8'))
return manipulator.hexdigest()


# 生成1-9之间的五个随机数
action = "".join([str(randint(1, 9)) for _ in range(5)])
print(action)
# 生成当前时间戳
tim = round(time())
# 生成5个随机大写字母
randstr = "".join(sample([chr(_) for _ in range(65, 91)], 5))
# 3个参数拼接之后进行MD5加密
value = action + str(tim) + randstr
hexs = hex5(value)
print(action, tim, randstr, hexs)
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
from time import time
from random import randint, sample
import hashlib
import requests


def hex5(value):
manipulator = hashlib.md5()
manipulator.update(value.encode('utf-8'))
return manipulator.hexdigest()


# 生成1-9之间的五个随机数
action = "".join([str(randint(1, 9)) for _ in range(5)])
print(action)
# 生成当前时间戳
tim = round(time())
# 生成5个随机大写字母
randstr = "".join(sample([chr(_) for _ in range(65, 91)], 5))
# 3个参数拼接之后进行MD5加密
value = action + str(tim) + randstr
hexs = hex5(value)
print(action, tim, randstr, hexs)


def uri():
args = '?actions={}&tim={}&randstr={}&sign={}'.format(action, tim, randstr, hexs)
return args

url = 'http://www.porters.vip/verify/sign/fet' + uri()

print(url)
resp = requests.get(url)
print(resp.status_code, resp.text)

本次的反爬虫利用 JavaScript 生成随机值,与之前的随机值不同,这次的随机值中包含时间戳和 MD5 加密值。签名验证有很多种实现方式,但原理都是相同的:由客户端生成一些随机值和不可逆的 MD5 加密字符串,并在发起请求时将这些值发送给服务器端。服务器端使用相同的方式对随机值进行计算以及 MD5 加密,如果服务器端得到的MD5 值与前端提交的 MD5 值相等,就代表是正常请求,否则返回403。

动态渲染反爬虫

动态网页比静态网页更具交互性,能给用户提供更好的体验。动态网页中常见的表现形式有下拉刷新、点击切换和悬停显示等。由JavaScript 改变 HTML DOM 导致页面内容发生变化的现象称为动态渲染。很多时候开发者只是想完成某个交互功能,而不是特意区分正常用户和爬虫程序,但这在不经意间限制了爬虫对数据的获取。由于编程语言没有像浏览器一样内置 JavaScript 解释器和渲染引擎,所以动态渲染是天然的反爬虫手段。

Selenium套件

Selenium 是一个用于测试Web应用程序的工具。我们可以通过Selenium 和浏览器驱动调用浏览器执行特定的操作,如发起网络请求、点击操作、鼠标下滑等。由于调用的是浏览器,所以这种组合还具备了资源自动加载和渲染的能力。

1
2
3
4
5
6
7
8
9
10
11
from selenium.webdriver import Chrome
from selenium.webdriver.common.by import By
import time
url = 'http://www.porters.vip/verify/sign/'

web = Chrome()
web.get(url)
web.find_element(By.XPATH, '//*[@id="fetch_button"]').click()
time.sleep(1)
resp = web.page_source
print(resp)

异步渲染库Puppeteer

在 Selenium 套件的支持下,我们很轻松地完成了爬取任务。但Selenium 套件也有一定的缺陷,当我们使用 Python 中的异步库编写爬虫时,Selenium 就不是那么适合了。异步是目前提升爬虫效率的常用手段之一,越来越多的人将同步的爬虫代码改为异步。由于浏览器是以进程的方式启动,所以它无法满足异步爬虫的渲染需求,为什么这么说呢?异步爬虫可以做到每秒向 300 个页面发起请求,但是要在计算机中开启 300 个浏览器进程或者 300 个标签页是很困难的,这会导致阻塞,影响异步爬虫程序的整体效率。我们可以用 Python 代
码验证一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from selenium import webdriver
from datetime import datetime

# 记录开始时间
starts = datetime.now()
url = 'http://www.porters.vip/verify/sign/'
# 初始化浏览器对象
browser = webdriver.Chrome()
for i in range(30):
# 循环30次访问目标url
browser.get(url)
resp = browser.page_source
browser.quit()
# 计算并打印耗时秒数
runtime = datetime.now() - starts
print(runtime.total_seconds())

selenium循环访问示例页面总共耗时7秒左右

Puppeteer是 Google 开源的 Node 库,它提供了一个高级 API来控制 Chrome 浏览器,浏览器中大多数手动执行的操作都可以使用Puppeteer 完成,更重要的是 Puppeteer 支持异步。Puppeteer 是一个 Node 库,如果你的爬虫程序是用 Node.js编写的,那么可以直接使用这个库,如果爬虫程序是用 Python 编写的,那么就需要用支持Python 的库 Pyppeteer。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import asyncio
from pyppeteer import launch


async def main():
# 初始化浏览器对象
browser = await launch()
# 在浏览器上下文中创建新页面
page = await browser.newPage()
# 打开目标网址
await page.goto('http://www.porters.vip/verify/sign')
# 点击指定按钮
await page.click('#fetch_button')
# 读取页面指定位置的文本
resp = await page.xpath('//*[@id="content"]')
text = await(await
resp[0].getProperty('textContent')).jsonValue()
print(text)
# 关闭浏览器对象
await browser.close()
asyncio.get_event_loop().run_until_complete(main())

说明 Puppeteer 也可以完成点击操作和页面渲染任务。

异步渲染服务 Splash

Splash 是一个异步的 JavaScript 渲染服务,它是带有 HTTP API 的轻量级 Web 浏览器。 Splash能够并行地处理多个页面请求,在页面上下文中执行自定义的 JavaScript 以及模浏览器中的点击、下滑等操作。有了 Splash 之后情况就变得不一样了,我们可以将Splash 服务部署到云服务器上并配置负载均衡。

Splash 服务提供了一个可视化的操作界面

image-20221013151514999

界面左侧是一些介绍文字,右侧是命令框。我们可以在右侧地址栏输入网址,点击Render me按钮后 Splash 就会向该网址发出请求。在右侧命令框中是 Splash 预设的示例代码,这段代码的作用是向指定网址发出请求,并在等待 0.5 秒后将页面文本、页面截图和资源加载相关信息返回。我们可以在可视化界面中尝试访问http://www.porters.vip/verify/sign,然后点击对应的按钮并打印渲染后的文本内容。

image-20221013151559253

更改命令行代码,点击按钮,提取网页文本

1
2
3
4
5
6
7
8
9
10
11
12
function main(splash, args)
-- 访问指定的url
assert(splash:go(args.url))
-- 按钮定位
local butt = splash:select('#fetch_button')
-- 点击按钮
butt:mouse_click()
content = splash:select('#content'):text()
return {
results = content
}
end

image-20221013151912483

Splash 允许我们编写自定义的 Lua 脚本,如果要获取脚本运行结果,那么我们可以使用execute 接口,execute 的详细介绍可参考Splash 文档中的 Splash Script 教程

详见:https://splash.readthedocs.io/en/stable/scriptingtutorial.html#scripting-tutorial

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
import requests
import json

# Splash接口
render = 'http://192.168.136.133:8050/execute'
# 需要执行的命令
script = """
function main(splash)
splash:go('http://www.porters.vip/verify/sign/')
local butt = splash:select('#fetch_button')
butt:mouse_click()
content = splash:select('#content'):text()
return {
result = content
}
end
"""

# 设置请求头
header = {'content-type': 'application/json'}
# 按照Splash规定提交命令
data = json.dumps({"lua_source": script})
resp = requests.post(render, data=data, headers=header)
# 打印返回的json
print(resp.json())

Splash 渲染服务同样可以完成点击事件和目标内容的提取。

图灵社区

1
<input type="search" name="q" placeholder="技术改变世界 阅读塑造人生"class="key">
Selenium
1
2
3
4
5
6
7
8
from selenium import webdriver
url = 'http://www.ituring.com.cn/'
# 初始化浏览器对象
browser = webdriver.Chrome()
# 向指定网址发起GET请求
browser.get(url)
# 使用CSS选择器定位搜索框,并输入文字
browser.find_element_by_css_selector('.key').send_keys('Python')
Puppeteer
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import asyncio
from pyppeteer import launch


async def main():
# 初始化浏览器对象
browser = await launch()
# 在浏览器上下文中创建新页面
page = await browser.newPage()
# 打开目标网站
await page.goto('http://www.ituring.com.cn')
# 在指定位置输入文本
await page.type('.key', 'python')
# 截图并报错为ituring.png
await page.screenshot({'path': 'ituring.png'})
# 关闭浏览器对象
await browser.close()

asyncio.get_event_loop().run_until_complete(main())
Splash
1
2
3
4
5
6
7
8
9
10
11
12
13
function main(splash, args)
assert(splash:go(args.url))
assert(splash:wait(0.2))
--聚焦搜索框
splash:select('input[name=q]'):focus()
--在搜索框中输入Python
splash:send_text('Python')
assert(splash:wait(0.2))
return {
png = splash:png()
}
end

Splash 页面左侧输出了图灵社区页面截图,并且截图中的搜索框文本内容也变成了Python。运行结果说明这3种工具都能够模拟人类对浏览器的操作。

设置禁止渲染工具加载图片或其他类型文件

书228页

图片伪装反爬虫

http://www.porters.vip/confusion/recruit.html

image-20221013160449463

电话这里是一张图片

任务:爬取企业名称及联系电话

企业名称

1
2
3
4
5
6
7
8
9
import requests
from parsel import Selector

url = 'http://www.porters.vip/confusion/recruit.html'

resp = requests.get(url)
sel = Selector(resp.text)
company = sel.css('h1.interval::text').get()
print(company)

电话号码用ocr识别

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import io
import requests
from urllib.parse import urljoin
from parsel import Selector
from PIL import Image
import pytesseract

url = 'http://www.porters.vip/confusion/recruit.html'
resp = requests.get(url)
sel = Selector(resp.text)

# 提取图片名称
image_name = sel.css('.pn::attr("src")').extract_first()
# 拼接图片名和URL
image_url = urljoin(url, image_name)
# 请求图片,拿到图片字节流内容
image_body = requests.get(image_url).content
# 使用Image.open打开图片字节流,得到图片对象
image_stream = Image.open(io.BytesIO(image_body))
print(pytesseract.image_to_string(image_stream))

css偏移反爬虫

http://www.porters.vip/confusion/flight.html

image-20221013164736265

image-20221013164754902

大概就是把上面的数排列好,然后用上面的多少px去对应他,第几位就是几,然后把所有数写出来,为764,倒序一下就位467

大概是这样,

WebDriver 识别

http://www.porters.vip/features/webdriver.html

借助”其实是通过对应的浏览器驱动(即WebDriver)向浏览器发出指令的行为。也就是说,开发者可以根据客户端是否包含浏览器驱动这一特征来区分正常用户和爬虫程序。

任务:爬取新闻专题页中的文章内容

selenium和pyppeteer无法获取目标数据

navigator.webdriver判断了是否使用webdriver驱动

绕过方法

Splash

对于 Splash 这种使用 WebKit 内核开发的渲染工具来说是无效的。我们可以用 Splash 获取示例 8 中的目标数据,Splash 脚本如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function main(splash, args)
assert(splash:go(args.url))
assert(splash:wait(0.5))
-- 定位按钮
local bton = splash:select('.btn.btn-primary.btn-lg')
assert(splash:wait(1))
-- 鼠标悬停
bton:mouse_hover()
-- 点击按钮
bton:mouse_click()
assert(splash:wait(1))
return {
-- 返回页面截图
png = splash:png(),
}
end

识别navigator.webdriver值,可以在触发之前将值改为false 或者undefined 即可。

Selenium 套件和 Puppeteer 都提供了运行 JavaScript 代码的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from selenium.webdriver import Chrome
from selenium.webdriver.common.by import By
import time

browser = Chrome()
browser.get('http://www.porters.vip/features/webdriver.html')

script = 'Object.defineProperty(navigator, "webdriver", {get: () => false,});'
# 运行javascript代码
browser.execute_script(script)
time.sleep(1)
# 定位按钮并点击
browser.find_element(By.CSS_SELECTOR, '.btn.btn-primary.btn-lg').click()
# 定位到文章内容
element = browser.find_element(By.CSS_SELECTOR, '#content')
time.sleep(1)

print(element.text)
browser.close()

修改之后的javascript只对当前的页面有效

浏览器特征

比如将浏览器请求头中的User-Agent 值与navigator.userAgent 属性值进行对比,结合navigator.platform 就可以判断客户端是否使用随机切换的UserAgent。

image-20221013173453628

image-20221013173458374

爬虫特征

访问频率限制

http://www.porters.vip/features/rate.html

1
2
3
4
import requests
for i in range(10):
resp = requests.get('http://www.porters.vip/features/rate.html')
print(resp.status_code)

直接访问4个503

方法1:加time.sleep(1)

方法2:分布式爬虫

方法3:代理池

浏览器指纹

1
2
3
4
<script>
console.log("userAgent:" + navigator.userAgent);
console.log("platform:" + navigator.platform);
</script>

获取ua和设备信息,判断客户端是否使用随机切换的User-Agent

Splash
1
2
3
4
5
6
7
8
9
10
11
import asyncio
from pyppeteer import launch
async def main():
browser = await launch()
page = await browser.newPage()
await page.goto('http://www.porters.vip/features/browser.html')
await page.setViewport({'width': 1000, 'height': 1000})
await page.screenshot({'path': 'browser.png'})
await browser.close()

asyncio.get_event_loop().run_until_complete(main())

Splash用linux搭建的,就会显示为linux

image-20221014110503956

Puppeteer截图

image-20221014110526892

image-20221014110531666

如果判断比较明显的客户端特征,比较离谱的就是爬虫的客户端,可以做为特征。

字符验证码

网址 :http://www.porters.vip/captcha/words.html
任务 :使用程序通过登录界面中的字符验证码校验。

保存验证码图片,尝试识别

1
2
3
4
5
6
7
import pytesseract
from os import path

# 保存在本地的验证码图片
images = path.join(path.dirname(path.abspath(__file__)), 'images/words.png')
# 使用 pytesseract库识别验证码中的字符并打印
print(pytesseract.image_to_string(images))

返回为空,说明并没有被识别

本节所面对的是带有彩色背景斜线和噪点图片,而且图片中的字符颜色与背景色并没有强烈的反差,这些因素都会影响识别效果。要想提高识别的成功率,我们必须对图片进行处理,例如降低斜线和噪点对文字的干扰,增强背景色与字符颜色的反差。也就是说,我们需要对图片进行灰度处理(去掉彩色)和二值化处理(降低干扰、增强颜色反差)。

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
import pytesseract
from os import path
from PIL import Image


def handler(grays, threshold=190):
"""
对灰度图片进行二值化处理
默认阈值为160,可根据实际情况调整
"""
table = []
for i in range(256):
if i < threshold:
table.append(0)
else:
table.append(1)

anti = grays.point(table, '1')
return anti


# 保存在本地的验证码图片
images = path.join(path.dirname(path.abspath(__file__)), 'images/words.png')
# 使用 pytesseract库识别验证码中的字符并打印
# 图片灰度处理
gray = Image.open(images).convert('L')

# 二值化处理
images = handler(gray)
images.show()
print(pytesseract.image_to_string(images))

测试 190的二值化处理最合适,但是googleocr依旧没办法识别

卷积网络

跳过

计算型验证码

网址 :http://www.porters.vip/captcha/mathes.html
任务 :使用程序通过登录页面中的计算型验证码校验。

1
2
3
4
5
6
7
8
from PIL import Image
import pytesseract
from os import path

# 保存在本地的验证码图片路径
images = path.join(path.dirname(path.abspath(__file__)), 'images/mathes.png')
# 使用pytesseract库识别图中的计算题并打印
print(pytesseract.image_to_string(images))

能正常识别

提权其中的数字和运算符

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
from PIL import Image
import pytesseract
from os import path
import re

# 保存在本地的验证码图片路径
images = path.join(path.dirname(path.abspath(__file__)), 'images/mathes.png')
# 将识别结果复制给 strings
strings = pytesseract.image_to_string(images)
# 从识别结果中提取数字
string = re.findall('\d+', strings)
# 从识别结果中提取运算符
operator = re.findall('[+|\-|\*]', strings)


def operator_func(a: int, b: int, oper: str) -> int:
# 接收两个值和运算符,返回数学运算结果
if oper == '+':
return a + b
if oper == '-':
return a - b
if oper == '*':
return a * b


# 将识别结果传入运算方法,获得运算结果
res = operator_func(int(string[0]), int(string[1]), operator[0])
print(res)

测试

image-20221014120040241

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
44
45
46
47
48
49
50
51
from PIL import Image
import pytesseract
from os import path
import re

# 保存在本地的验证码图片路径
images = path.join(path.dirname(path.abspath(__file__)), 'images/mathes2.png')


def handler(grays, threshold=30):
"""
对灰度图片进行二值化处理
默认阈值为160,可根据实际情况调整
"""
table = []
for i in range(256):
if i < threshold:
table.append(0)
else:
table.append(1)

anti = grays.point(table, '1')
return anti


gray = Image.open(images).convert('L')
# 二值化处理
images = handler(gray)
images.show()


# 将识别结果复制给 strings
def operator_func(a: int, b: int, oper: str) -> int:
# 接收两个值和运算符,返回数学运算结果
if oper == '+':
return a + b
if oper == '-':
return a - b
if oper == '*':
return a * b


strings = pytesseract.image_to_string(images)
# 从识别结果中提取数字
string = re.findall('\d+', strings)
# 从识别结果中提取运算符
operator = re.findall('[+|\-|\*]', strings)

# 将识别结果传入运算方法,获得运算结果
res = operator_func(int(string[0]), int(string[1]), operator[0])
print(res)

能识别

滑动验证码

网址 :http://www.porters.vip/captcha/sliders.html
任务 :使用程序通过登录界面中滑动验证码的校验

流程:

  1. 打开页面鼠标移动到滑块位置
  2. 按下鼠标将滑块拖拽到滑轨终点位置
  3. 放开鼠标,等待验证结果
Selenium
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import time

from selenium.webdriver import Chrome
from selenium import webdriver
from selenium.webdriver.common.by import By

browser = Chrome()

browser.get('http://www.porters.vip/captcha/sliders.html?')

# 输入账号密码
browser.find_element(By.XPATH, '//*[@id="inputEmail3"]').send_keys('123123@qq.com')
browser.find_element(By.XPATH, '//*[@id="inputPassword3"]').send_keys('123123123')

# 定位滑块
hover = browser.find_element(By.CSS_SELECTOR, '.hover')

action = webdriver.ActionChains(browser)
action.click_and_hold(hover).perform() # 点击并保持不松开
action.move_by_offset(340, 0) # 设置滑动距离,横向距离为340px,纵向距离为0px
action.release().perform() # 松开鼠标
time.sleep(1)
browser.find_element(By.XPATH, '/html/body/div[2]/div[2]/form/div[4]/div/button').click()
time.sleep(1)

滑动拼图验证码

网址 :http://www.porters.vip/captcha/jigsaw.html
爬虫任务 :使用程序通过滑动拼图验证码的校验。

滑块和滑块的区域是可以获取到的,提取里面的值,后者减去前者,移动距离。

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
from selenium import webdriver
from selenium.webdriver.common.by import By
import re
from parsel import Selector

web = webdriver.Chrome()
web.get('http://www.porters.vip/captcha/jigsaw.html')

# 定位滑块
jigsawCircle = web.find_element(By.CSS_SELECTOR, '#jigsawCircle')
action = webdriver.ActionChains(web)
action.click_and_hold(jigsawCircle).perform()
# 返回当前页面的html代码
html = web.page_source

sel = Selector(html)
# 获取圆角矩形和缺口的CSS样式
mbk_style = sel.css('#missblock::attr("style")').get()
tbk_style = sel.css('#targetblock::attr("style")').get()
# 编写用于从css样式中提取left属性值的匿名函数
extract = lambda x: ''.join(re.findall('left: (\d+|\d+.\d+)px', x))
# 调用匿名函数获取css样式中的left属性值
mbk_style = extract(mbk_style)
tbk_style = extract(tbk_style)
# 计算当前拼图验证码滑块所需移动的距离
distance = float(tbk_style) - float(mbk_style)
action.move_by_offset(distance, 0) # 设置滑动距离
action.release().perform() # 松开鼠标

文字点选验证码

网址 :http://www.porters.vip/captcha/clicks.html
任务 :编写程序通过登录页面的文字点选验证码校验。

涉及深度学习,暂时跳过

获取需要识别的文字

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import re
import time
from selenium import webdriver
from parsel import Selector


url = 'http://www.porters.vip/captcha/clicks.html'
web = webdriver.Chrome()
web.get(url)
time.sleep(1)
html = Selector(web.page_source)
# 获取验证要求
print(html)
require = html.css('#divTips::text').get()
# 用正则提取验证要求中的文字
target = re.findall('"(.)"', require)
print(target)