Bootstrap

1. 爬虫之Beautifulsoup解析库&在线解析图片验证码

1. 解析库beautifulsoup

1.1 介绍
BeautifulSoup是一个可以从HTML或XML文件中提取数据的Python库.
官方文档: https://www.crummy.com/software/BeautifulSoup/bs4/doc/index.zh.html
1.2 解析库
Python2.7.3之前的版本和Python3中3.2.2之前的版本, 必须安装lxml或html5lib, 
因为那些Python版本的标准库中内置的HTML解析方法不够稳定.
解析器使用方法优势劣势
Python标准库BeautifulSoup(markup, “html.parser”)Python的内置标准库执行速度适中文档容错能力强Python 2.7.3 or 3.2.2)前 的版本中文档容错能力差
lxml HTML 解析器BeautifulSoup(markup, “lxml”)速度快文档容错能力强需要安装C语言库
lxml XML 解析器BeautifulSoup(markup, [“lxml”, “xml”])``BeautifulSoup(markup, “xml”)速度快唯一支持XML的解析器需要安装C语言库
html5libBeautifulSoup(markup, “html5lib”)最好的容错性以浏览器的方式解析文档生成HTML5格式的文档速度慢不依赖外部
安装BeautifulSoup4与解析器:
pip install BeautifulSoup4
pip install lxml
pip install html5lib

生成节点 Tag对象:
from bs4 import BeautifulSoup
soup = BeautifulSoup(需要解析的字符串, 使用的解析器)
1.3 容错处理
文档的容错能力: 指的是在html代码不完整的情况下, 使用该模块可以识别该错误。
使用BeautifulSoup解析上述代码, 能够得到一个 BeautifulSoup 的对象, 并能按照标准的缩进格式的结构输出.
from bs4 import BeautifulSoup

# html字符串
html_doc = """
<html><head><title>页面标题</title></head>
<body>
<p class="title"><b>标题</b></p>

<p class="story">开始,
<a href="https://www.baidu.com" class="sister" id="link1">A</a>
<a href="https://www.baidu.come" class="sister" id="link2">B</a> 中间
<a href="https://www.baidu.com" class="sister" id="link3">C</a>
结束.</p>

<p class="story">...</p>
"""

soup = BeautifulSoup(html_doc, 'lxml')  # 具有容错功能
res = soup.prettify()  # 处理好缩进, 结构化显示
print(res)

1.4 遍历文档树
遍历文档树: 即直接通过标签名字选择, 特点是选择速度快, 但如果存在多个相同的标签则只返回第一个.
* 多个相同标签只返回第一个, 第一个算遍历, 其他的都不算!
1. 获取标签对象
soup = BeautifulSoup(html_doc, 'lxml')
# 与.find()方法速度一致
print(soup.p)  # <p class="title"><b>标题</b></p>
# 值是提供Tag对象

# 推荐方法(速度快)
print(soup.body.p)  # <p class="title"><b>标题</b></p>
2. 获取标签名称
soup = BeautifulSoup(html_doc, 'lxml')  
print(soup.body.p.name)  # p

3. 获取标签的属性
soup = BeautifulSoup(html_doc, 'lxml')  
print(soup.body.p.attrs)  # {'class': ['title']}  值是一个列表
print(soup.body.p.attrs['class'])  # ['title']
print(soup.body.p['class'])  # ['title']

4. 获取标签的内容
* 1. 文本内容
soup = BeautifulSoup(html_doc, 'lxml')
print(soup.body.p.text)  # 标题
print(soup.body.p.get_text()) 

soup = BeautifulSoup(html_doc, 'lxml')
# 获取第二个p标签, next_sibling下一个兄弟标签, 空行算一个next_sibling
print(soup.body.p.next_sibling.next_sibling.text)
"""
开始,
A
B 中间
C
结束.
"""
如果tag包含了多个子节点, tag就无法确定 
.string 方法应该调用哪个子节点的内容, .string 的输出结果是 None.
如果只有一个子节点那么就输出该子节点的文本,比如下面的这种结构, .string 返回为None,
.strings就可以找到所有文本, 值是一个生成器.
soup = BeautifulSoup(html_doc, 'lxml')

# 第一个p标签
print(soup.body.p.string)
# 第二个p标签, 标签下文本只有一个, 没有其他标签时, 可以取到值, 否则为None
print(soup.body.p.next_sibling.next_sibling.string)
# 第一个p标签
print(soup.body.p.strings)  # <generator object Tag._all_strings at 0x0000017A63065740>
print(list(soup.body.p.strings))  # ['标题']
# 第二个p标签
print(list(soup.body.p.next_sibling.next_sibling.strings))  # ['开始,\n', 'A', '\n', 'B', ' 中 间\n', 'C', '\n结束.']
soup = BeautifulSoup(html_doc, 'lxml')

for line in soup.body.p.next_sibling.next_sibling.stripped_strings:  # 去掉空白
    print(line)
"""
开始,
A
B
中 间
C
结束.
"""
5. 嵌套选择
soup = BeautifulSoup(html_doc, 'lxml')

print(soup.head.title.text)  
print(soup.body.a.text)

6. 子节点&子孙节点
soup = BeautifulSoup(html_doc, 'lxml')

print(soup.body.p.contents)  # p下所有子节点
print(soup.body.p.children)  # 得到一个迭代器,包含p下所有子节点

for i, child in enumerate(soup.p.children):
    print(i, child)

print(soup.p.descendants)  # 获取子孙节点, p下所有的标签都会选择出来

for i, child in enumerate(soup.p.descendants):
    print(i, child)
7. 父节点&祖先节点
soup = BeautifulSoup(html_doc, 'lxml')

print(soup.a.parent)  # 获取a标签的父节点
print(soup.a.parents)  # 找到a标签所有的祖先节点,父亲的父亲,父亲的父亲的父亲...
for a in soup.a.parents:
    print(a, '\n')  # 会有两个html标签

8. 兄弟节点
* 空行被算成一个兄弟.
soup = BeautifulSoup(html_doc, 'lxml')

print(soup.body.a.next_sibling)  # 下一个兄弟
print(soup.body.a.previous_sibling)  # 上一个兄弟

print(list(soup.body.a.next_siblings))  # 下面的兄弟们=>生成器对象
print(soup.body.a.previous_siblings)  # 上面的兄弟们=>生成器对象
1.5 搜索文档树
1. 五种过滤器
五种过滤器: 字符串, 正则表达式, 列表, True/False, 函数方法
* 1. 字符串过滤()
     find(): 找到一个就放回
     find_all(): 找多个
     ...
soup = BeautifulSoup(html_doc, 'lxml')


print(soup.find('p'))  # 标签查找
print(soup.find(name='p'))  # 结果是一个Tag对象

print(soup.find(class_='title'))  # 类查找
print(soup.find(attrs={'class': 'title'}))

print(soup.find(id="link1"))  # id查找
print(soup.find(attrs={'id': 'link1'}))
print(soup.find(href="https://www.baidu.com"))  # 属性查找

# 条件可以有多个 为 and关系
print(soup.find(id="link1", class_='sister'))



print(soup.find_all(name='p'))  # 结果是一个列表, 存放Tag对象
* 2. 正则表达式
import re

soup = BeautifulSoup(html_doc, 'lxml')
re_t = re.compile('^t')
print(soup.find(class_=re_t))  # <p class="title"><b>标题</b></p>
* 3. 列表
soup = BeautifulSoup(html_doc, 'lxml')
print(soup.find_all(class_=['title', 'sister']))

* 4. True/False
soup = BeautifulSoup(html_doc, 'lxml')
# 存在或不存在否个属性
print(soup.find(id=True))
print(soup.p.find(id=False))
print(soup.find(href=True))

* 5. 自定义函数
soup = BeautifulSoup(html_doc, 'lxml')


# 定义函数
def has_class_but_no_id(tag):
    return tag.has_attr('class') and not tag.has_attr('id')

print(soup.body.find_all(has_class_but_no_id))
* 6. limit 参数与 recursive参数
   limit: 限制条数
   recursive: 当前层级查询不到, 递归查询(默认开启)
soup = BeautifulSoup(html_doc, 'lxml')

print(soup.body.find_all(name='p', limit=1))

soup = BeautifulSoup(html_doc, 'lxml')

print(soup.body.find_all(name='b', recursive=False))

2. css选择器
select 返回的是列表形式.
soup = BeautifulSoup(html_doc, 'lxml')

print(soup.select('#link1'))

print(soup.select('body>p>b'))  

print(soup.select('body p b'))

1.6 修改文档树
* 1. 修改标签名称
soup = BeautifulSoup(html_doc, 'lxml')

soup.body.p.name = 'h3'
print(soup.body.h3.name)  # 将第一个p标签改为h3标签, html中没有h3会报错

* 2. 修改属性值
soup = BeautifulSoup(html_doc, 'lxml')

soup.body.p['class'] = 'c1'
print(soup.body.p.attrs)  # {'class': 'c1'}
* 3. 修改值
soup = BeautifulSoup(html_doc, 'lxml')

print(soup.body.p.string)  # 标题
soup.body.p.string = 'xxx'
print(soup.body.p.string)  # xxx

* 4. 添加值
soup = BeautifulSoup(html_doc, 'lxml')

print(soup.body.p.string)  # 标题
soup.body.p.string = 'xxx'
soup.body.p.append('abcd')  # 添加值
print(soup.body.p.string)  # None
print(soup.body.p.text)  # xxxabcd 

* 5. 删除标签
soup = BeautifulSoup(html_doc, 'lxml')
print(soup.body.p)  # 展示第一个标签
soup.body.p.decompose()   # 删除第一个p标签
print(soup.body.p)  # 展示原第二标签

2. 宝塔面板与JumpServer堡垒机

2.1 宝塔面板
宝塔: 宝塔面板是一款支持windows和linux系统的服务器管理软件, 可通过Web端管理服务器, 提升运维效率. 
使用宝塔前: 手工输入命令安装各类软件, 操作起来费时费力并且容易出错.
而且需要记住很多Linux的命令,非常复杂.

使用宝塔后: 2分钟装好面板, 一键管理服务器, 鼠标点几下就能替代以前的复杂繁多命令,
操作简单, 看一眼就会使用. (图形化替代命令行操作)
2.2 JumpServer堡垒机
JumpServer跳板机/堡垒机: 是一类可作为跳板批量操作远程设备的网络设备,
提供了认证、授权、审计和自动化运维等功能...

jumpserver组件说明
jumpserver堡垒机由以下三个部分组成:
* 1.jumpserver
jumpserver是jumpserver的核心组件, 是一个使用Python的django开发的管理后台, 支持restful API.
* 2.coco
coco是SSH Server和Web Terminal Server的组件,提供SSH和WebSocket接口, 使用paramiko和flask开发.
* 3.luna
luna是Web Terminal Server的前端, 前端页面均由该项目提供, 主要负责页面后台的渲染.

3. 使用bs4库爬虫实例

网站: https://www.autohome.com.cn/news/2/#liststart
需要获取: 标题, 图片, 简介, 链接
使用BeautifulSoup,从HTML文件中提取数据.
* 1. 分析网站

2022-06-16_121308

2022-06-16_131728

import requests
res = requests.get('https://www.autohome.com.cn/news/1/#liststart')
print(res.text)
得到的ul信息中有文章也有广告.
有h3标签的是文字, 没有h3标签的广告获取是空li标签.

2022-06-16_123624

广告

2022-06-16_130325

空标签

2022-06-16_131900

文章链接与封面路由

2022-06-16_133843

* 2. 使用BeautifulSoup获取html的ul数据.
import requests
from bs4 import BeautifulSoup
# 获取相应对象
res = requests.get('https://www.autohome.com.cn/news/1/#liststart')
# 生成文档对象 (html文件, 解析器)
soup = BeautifulSoup(res.text, 'html.parser')
# 查找ul的数据 查找class为article的标签数据, class是关键字, 加后缀_使用class_
# 得到一个Tag标签对象
ul = soup.find(class_='article')
print(ul)

2022-06-16_123935

* 3. 查询ul标签下所有的li标签
import requests
from bs4 import BeautifulSoup

# 获取相应对象
res = requests.get('https://www.autohome.com.cn/news/1/#liststart')
# 生成文档对象 (html文件, 解析器)
soup = BeautifulSoup(res.text, 'html.parser')
# 查找ul的数据 查找class为article的标签数据, class是关键字, 加后缀_使用class_
ul = soup.find(class_='article')
# 查找ul下的所有li标签, 得到一个列表
li_list = ul.find_all(name='li')
print(li_list)

2022-06-16_124931

* 4. 获取标题, 图片, 简介, 链接的信息.
import requests
from bs4 import BeautifulSoup

# 获取相应对象
res = requests.get('https://www.autohome.com.cn/news/1/#liststart')
# 生成文档对象 (html文件, 解析器)
soup = BeautifulSoup(res.text, 'html.parser')
# 查找ul的数据 查找class为article的标签数据, class是关键字, 加后缀_使用class_
ul = soup.find(class_='article')
# 查找ul下的所有li标签, 得到一个列表
li_list = ul.find_all(name='li')
for li in li_list:
    # 剔除非文章的li标签, 获取不到h3的都不是文字
    title = li.find(name='h3')  # 得到h3标签对象
    if title:
        # 获取标题
        title = title.text
        # 获取文章链接(链接缺少https:)
        article_url = 'https:' + li.find('a').attrs.get('href')
        # 获取封面, 查找img标签, .attrs获取标签的所有的属性, 值是一个字典. 通过get获取到值
        img_url = li.find(name='img').attrs.get('src')
        # 判断封面链接字符串是否是https开头(有一部分是, 有一部分不是)
        img_url = img_url if img_url.startswith('https:') else 'https:' + img_url
        # 获取文章简介
        desc = li.find('p').text
        print(
            f"""
标题: {title},
文章链接: {article_url},
封面: {img_url},
简介: {desc}
"""
        )

2022-06-16_134043

4. 创建代理池

* 1. 分析免费代理网站
    地址: http://www.66ip.cn/1.html

2022-06-17_063004

import requests

url = 'http://www.66ip.cn/1.html'

res = requests.get(url)
res.encoding = res.apparent_encoding
print(res.text)

* 2. 获取ip表单
import requests
from bs4 import BeautifulSoup

url = 'http://www.66ip.cn/1.html'

res = requests.get(url)
res.encoding = res.apparent_encoding

soup = BeautifulSoup(res.text, 'lxml')

# 获取表单的节点(页面中有多个表单标签, 需要的ip表单在最后一个)
print(soup.find_all(name='table')[-1])

<table width='100%' border="2px" cellspacing="0px" bordercolor="#6699ff">
    <tr>
        <td>ip</td>
        <td>端口号</td>
        <td>代理位置</td>
        <td>代理类型</td>
        <td>验证时间</td>
    </tr>
    <tr>
        <td>165.225.20.14</td>
        <td>10605</td>
        <td>宁夏回族自治区中卫市</td>
        <td>高匿代理</td>
        <td>2022年06月17日06时 验证</td>
    </tr>
    <tr>
        <td>8.215.27.71</td>
        <td>3128</td>
        <td>吉林省辽源市</td>
        <td>高匿代理</td>
        <td>2022年06月17日04时 验证</td>
    </tr>
    <tr>
        <td>103.155.54.245</td>
        <td>84</td>
        <td>广西壮族自治区桂林市</td>
        <td>高匿代理</td>
        <td>2022年06月17日02时 验证</td>
    </tr>
    <tr>
        <td>183.245.6.120</td>
        <td>8080</td>
        <td>云南省保山市</td>
        <td>高匿代理</td>
        <td>2022年06月17日00时 验证</td>
    </tr>
</table>
* 3. 获取表单中tr的标签信息
import requests
from bs4 import BeautifulSoup

url = 'http://www.66ip.cn/1.html'

res = requests.get(url)
res.encoding = res.apparent_encoding

soup = BeautifulSoup(res.text, 'lxml')

# 获取表单的节点(页面中有多个表单标签, 需要的ip表单在最后一个)
table = soup.find_all(name='table')[-1]

# 获取表单中的子节点, 遍历tr标签
for tr in table.children:
    print(tr)
* table.children 获取table下说有的子标签, 行号算一个标签!!!
正常的标签: <!--<class 'bs4.element.Tag'>-->
空的标签: <!--<class 'bs4.element.NavigableString'>-->
<!--<class 'bs4.element.NavigableString'>-->
<tr>
    <td>ip</td>
    <td>端口号</td>
    <td>代理位置</td>
    <td>代理类型</td>
    <td>验证时间</td>
</tr>
<!--<class 'bs4.element.Tag'>-->

<!--<class 'bs4.element.NavigableString'>-->
<tr>
    <td>165.225.20.14</td>
    <td>10605</td>
    <td>宁夏回族自治区中卫市</td>
    <td>高匿代理</td>
    <td>2022年06月17日06时 验证</td>
</tr>
<!--<class 'bs4.element.Tag'>-->
<tr>
    <td>8.215.27.71</td>
    <td>3128</td>
    <td>吉林省辽源市</td>
    <td>高匿代理</td>
    <td>2022年06月17日04时 验证</td>
</tr>
<!--<class 'bs4.element.Tag'>-->
<tr>
    <td>103.155.54.245</td>
    <td>84</td>
    <td>广西壮族自治区桂林市</td>
    <td>高匿代理</td>
    <td>2022年06月17日02时 验证</td>
</tr>
<!--<class 'bs4.element.Tag'>-->
<tr>
    <td>183.245.6.120</td>
    <td>8080</td>
    <td>云南省保山市</td>
    <td>高匿代理</td>
    <td>2022年06月17日00时 验证</td>
</tr>
<!--<class 'bs4.element.Tag'>-->
<!--<class 'bs4.element.NavigableString'>-->


* 4. 获取表单中tr的标签信息(剔除空行标签)
import requests
import bs4
from bs4 import BeautifulSoup

url = 'http://www.66ip.cn/1.html'

res = requests.get(url)
res.encoding = res.apparent_encoding

soup = BeautifulSoup(res.text, 'lxml')

# 获取表单的节点(页面中有多个表单标签, 需要的ip表单在最后一个)
table = soup.find_all(name='table')[-1]

# 获取表单中的子节点, 遍历tr标签
for tr in table.children:
    if isinstance(tr, bs4.element.Tag):
        print(tr)

* 5. 获取表单中的td标签
...

# 获取表单中的子节点, 遍历tr标签
for tr in table.children:
    if isinstance(tr, bs4.element.Tag):
        # 遍历tr下的td标签
        for td in tr.children:
            print(td)
        print('----')

<td>ip</td>
<td>端口号</td>
<td>代理位置</td>
<td>代理类型</td>
<td>验证时间</td>
----
<td>165.225.20.14</td>
<td>10605</td>
<td>宁夏回族自治区中卫市</td>
<td>高匿代理</td>
<td>2022年06月17日06时 验证</td>
* 6. 正则匹配获取ip与端口
import requests
import bs4
from bs4 import BeautifulSoup
import re

url = 'http://www.66ip.cn/1.html'

res = requests.get(url)
res.encoding = res.apparent_encoding

soup = BeautifulSoup(res.text, 'lxml')

# 获取表单的节点(页面中有多个表单标签, 需要的ip表单在最后一个)
table = soup.find_all(name='table')[-1]

# 获取表单中的子节点, 遍历tr标签
# 定义一个列表(链接池)
ip_port_list = []
for tr in table.children:
    if isinstance(tr, bs4.element.Tag):

        # tr.text会展示所有td的内容
        # ip正则
        re_ip = re.compile(r'^\d+.\d+.\d+.\d+$')
        ip = tr.find(text=re_ip)  # 匹配成功拿到值, 匹配不成功为None

        # port正则
        re_port = re.compile(r'^\d+$')
        port = tr.find(text=re_port)

        # 将ip与port放到列表中
        if ip and port:
            # 协议类型
            http_type_list = ['http', 'https']
            # 代理ip端口
            ip_port = ip + ":" + port

            for http_type in http_type_list:
                try:
                    # 测试是否可以正常使用成功在将ip与port存到链接值中(使用代理失败会报错)
                    res = requests.get('https://www.baidu.com/', proxies={http_type: ip_port}, timeout=10)

                    if res.status_code == 200:
                        ip_port_list.append((http_type, ip_port))
                        print('ok')
                # 网页中没有提示代理是http还是https
                except Exception as e:
                    pass
print(ip_port_list)
"""
[('http', '52.236.90.60:3128'), 
('http', '165.225.20.14:10605'),
('http', '8.215.27.71:3128'),
('http', '103.155.54.245:84'),
('http', '183.245.6.120:8080')]
"""

5. 第三链接池项目

* 1. 下载项目
     项目地址: https://github.com/jhao104/proxy_pool

2022-06-17_092729

* 2. 解压并打来项目
* 3. 安装依赖: pip install -r requirements.txt
# 安装不上就换成国内源
APScheduler==3.2.0  # 定时任务框架
werkzeug==0.15.5  # Python的WSGI规范的实用函数库
Flask==1.0
requests==2.20.0 
click==7.0  # 用于快速创建命令行
gunicorn==19.9.0  # Python WSGI UNIX的HTTP服务器
lxml  # lxml是XML和HTML的解析器
redis
* 4. 更新配置(配置好redis库即可)
# setting.py 为项目配置文件

# 配置API服务

HOST = "0.0.0.0"               # IP
PORT = 5010                    # 监听端口


# 配置数据库

DB_CONN = 'redis://:127.0.0.1:6379/0'


# 配置 ProxyFetcher

PROXY_FETCHER = [
    "freeProxy01",      # 这里是启用的代理抓取方法名,所有fetch方法位于fetcher/proxyFetcher.py
    "freeProxy02",
    # ....
]
* 5. 启动项目
       如果已经具备运行条件, 可用通过proxyPool.py启动
       程序分为: schedule 调度程序  server Api服务
# 都在Terminal 中输入命令
# 1. 启动调度程序(获取免费代理)
python proxyPool.py schedule

# 2. 启动webApi服务
python proxyPool.py server
启动报错:(更新 Flask Jinja2)
pip uninstall  Flask Jinja2
pip install Flask Jinja2
启动调度后获取免费代理

2022-06-17_100326

* 6. 测试

2022-06-17_100148

* 7. 随机获取一个请求

2022-06-17_100223

* 8. 请求api介绍
  启动web服务后, 默认配置下会开启 http://127.0.0.1:5010 的api接口服务:
apimethodDescriptionparams
/GETapi介绍None
/getGET随机获取一个代理可选参数: ?type=https 过滤支持https的代理
/popGET获取并删除一个代理可选参数: ?type=https 过滤支持https的代理
/allGET获取所有代理可选参数: ?type=https 过滤支持https的代理
/countGET查看代理数量None
/deleteGET删除代理?proxy=host:ip
* 删除代理  ?proxy=host:ip  ==> 官方文旦写的不好理解了,应该是 ?proxy=ip:port
* 9. 新建一个项目, 在项目中使用
import requests


# 获取代理
def get_proxy():
    # 后端发送的是json格式数据, 转为字段
    return requests.get("http://127.0.0.1:5010/get/").json()


# 删除代理
def delete_proxy(proxy):
    # 删除代理请求, 请求中携带删除的代理ip
    # http://127.0.0.1:5010/delete/?proxy=211.139.26.16:80
    requests.get("http://127.0.0.1:5010/delete/?proxy={}".format(proxy))


# 使用代理获取网页
def getHtml():
    # 重试次数
    retry_count = 5
    # 从字典中获取代理
    proxy = get_proxy().get("proxy")
    # print(proxy)  # 194.233.77.110:1111
    while retry_count > 0:
        # 代理使用出错会抛出异常
        try:
            html = requests.get('http://www.baidu.com', proxies={"http": "http://{}".format(proxy)})
            # 使用代理访问
            
            return html
        except Exception:
            retry_count -= 1
    # 5次删除代理池中代理
    delete_proxy(proxy)
    return None


# 执行函数
html = getHtml()
print(html.status_code)  # 200

6. 打码平台使用

只演示图片验证码: 
* 1. 将验证码下载到本地
* 2. 将验证码上传到打码平台(将图片验证码给别人识别, 全国有几十台服务器, 有六千多工人, 24小时轮班...)
* 3. 打码平台放回验证码
验证码识别平台: http://www.chaojiying.com/

* 1. 注册一个账户 
* 2. 下载Demo实例代码包

2022-06-17_115724

* 3. 解压代码包
    Chaojiying_Python
     |-a.jpg   验证码图片
     |-chaojiying.py 实例代码

2022-06-17_120017

* 4. 生成软件id

2022-06-17_121612

2022-06-17_121718

2022-06-17_121749

* 5. 获取积分

2022-06-17_122239

* 6. 打开项目测试

image-20220617122517936

#!/usr/bin/env python
# coding:utf-8

import requests
from hashlib import md5


# 定义类
class Chaojiying_Client(object):
    # 实例化生成用户信息与请求头信息
    def __init__(self, username, password, soft_id):
        # 用户
        self.username = username
        # 密码解码
        password = password.encode('utf8')
        # 加密密码
        self.password = md5(password).hexdigest()
        # 应用id
        self.soft_id = soft_id
        # 基本参数
        self.base_params = {
            'user': self.username,
            'pass2': self.password,
            'softid': self.soft_id,
        }
        # 请求头信息
        self.headers = {
            'Connection': 'Keep-Alive',
            'User-Agent': 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0)',
        }

    # 将验证码图片发送到打码平台识别
    def PostPic(self, im, codetype):
        """
        im: 图片字节
        codetype: 题目类型 参考 http://www.chaojiying.com/price.html
        """
        # 图片的类型
        params = {
            'codetype': codetype,
        }
        # 将用户基本信息与请求头信息写入字典中
        params.update(self.base_params)
        # 读取图片
        files = {'userfile': ('ccc.jpg', im)}
        # 发送请求到打码平台
        r = requests.post('http://upload.chaojiying.net/Upload/Processing.php', data=params, files=files,
                          headers=self.headers)
        # 返回一个json格式字典数据, 解码
        return r.json()

    # 将验证码图片发送到打码平台识别 base64格式数据
    def PostPic_base64(self, base64_str, codetype):
        """
        im: 图片字节
        codetype: 题目类型 参考 http://www.chaojiying.com/price.html
        """
        params = {
            'codetype': codetype,
            'file_base64': base64_str
        }
        params.update(self.base_params)
        r = requests.post('http://upload.chaojiying.net/Upload/Processing.php', data=params, headers=self.headers)
        return r.json()

    # 异常信息
    def ReportError(self, im_id):
        """
        im_id:报错题目的图片ID
        """
        params = {
            'id': im_id,
        }
        params.update(self.base_params)
        r = requests.post('http://upload.chaojiying.net/Upload/ReportError.php', data=params, headers=self.headers)
        return r.json()


if __name__ == '__main__':
    # 生成对象
    chaojiying = Chaojiying_Client('q18177', 'q18177', '93518')
    # 用户中心>>软件ID 生成一个id替换
    im = open('a.jpg', 'rb').read()  
    # 本地图片文件路径 来替换 a.jpg 有时WIN系统须要//
    print(chaojiying.PostPic(im, 1902))  
    # 1902 验证码类型  官方网站>>价格体系 3.4+版 print 后要加()
    # print chaojiying.PostPic(base64_str, 1902)  #此处为传入 base64代码

* 7. 查看结果pic_str是验证码

image-20220617122444430

7. 打码平台使用案例

* 1. 分析登入(输入一个错误的密码, 不然看不到请求就跳转了)
登入地址: http://www.aa7a.cn/user.php?&ref=http%3A%2F%2Fwww.aa7a.cn%2F
获取验证码图片
* 这个网站的验证码就是不检验的,一下找不到合适的网站测试

2022-06-17_131703

import requests
from bs4 import BeautifulSoup

res = requests.get('http://www.aa7a.cn/user.php?&ref=http%3A%2F%2Fwww.aa7a.cn%2F')
soup = BeautifulSoup(res.text, 'lxml')

# 获取图片地址
url = soup.find(id='login_img_checkcode')['src']
code_url = 'http://www.aa7a.cn/' + url
print(code_url)

* 2. 登入触发事件
    数据提交地址: http://www.aa7a.cn/user.php
    提交数据:
    username: 1360012768@qq.com
    password: zxc12dasdasd
    captcha: asda
    remember: 1
    ref: http://www.aa7a.cn
    act: act_login

2022-06-17_132015

2022-06-17_132218

import requests
from bs4 import BeautifulSoup
# 导入打码平台
from chaojiying import Chaojiying_Client

res = requests.get('http://www.aa7a.cn/user.php?&ref=http%3A%2F%2Fwww.aa7a.cn%2F')
soup = BeautifulSoup(res.text, 'lxml')

# 获取图片地址
url = soup.find(id='login_img_checkcode')['src']
code_url = 'http://www.aa7a.cn/' + url

# 获取验证码码
chaojiying = Chaojiying_Client('q18177354117', 'q18177354117', '935180')  # 用户中心>>软件ID 生成一个替换 96001
# 保存图片
res = requests.get(code_url)
code_dict = chaojiying.PostPic(res.content, 1902)
print(code_dict.get('pic_str'))

# data数据
data = {
    'username': '[email protected]',
    'password': 'zxc123456',
    'captcha': code_dict.get('pic_str'),
    'remember': 1,
    'ref': 'http://www.aa7a.cn/',
    'act': 'act_login',
}

# 登入
res = requests.post('http://www.aa7a.cn/user.php', data=data)
print(res.text)
# 登入成功显示: {"error":0,"ref":"http: // www.aa7a.cn"}
# 登入失败显示: {"error":5}

8. bs4爬虫小说

* 1. 分析小说章节名称
    地址: https://www.au26.com/shu/3414/
    分析小说章节名称, 文章内容.

2022-06-19_124538

2022-06-19_125157

2022-06-19_125453

2022-06-19_125800

末尾还有两种广告!

2022-06-19_144007

2022-06-19_144030

* 2. 爬虫程序
import requests
from bs4 import BeautifulSoup
import re

# 获取小说章节
res = requests.get('https://www.au26.com/shu/3414/')
soup = BeautifulSoup(res.text, 'lxml')

# 获取小说名称
name = soup.h1.text

# 获取到id为list的节点
id_list = soup.find(id='list')

# 排查广告标签 留下 <a href="/shu/3414/779395.html">第326章 完本感言</a> 这样的标签
chapter_re_href = re.compile(r'(.*?).html')
chapter_re_text = re.compile(r'第\d+章.*')

# 获取list下所有的a标签
list_a = id_list.find_all(href=chapter_re_href, text=chapter_re_text)

# 去重并排序 [3, 2, 1, 2, 3, 4] -> [1, 2, 3, 4]
chapter_list = []
for chapter in list_a:
    # 存在则更新 chapter 是Tag对象 不是字符串!!!
    if chapter in chapter_list:
        # 获取存在值的索引
        index = chapter_list.index(chapter)
        # pop掉
        chapter_list.pop(index)
        # 重新把值写在后面
        chapter_list.append(chapter)

    chapter_list.append(chapter)

print(chapter_list)

# 写入txt
with open(f'{name}.txt', mode='wt', encoding='utf-8') as wf:
    # chapter 是Tag对象 不是字符串!!!
    for chapter in chapter_list:
        # 章节名称
        chapter_name = chapter.text
        # 章节地址
        chapter_url = 'https://www.au26.com/' + chapter.get('href')

        # 章节内容
        res = requests.get(chapter_url)
        soup = BeautifulSoup(res.text, 'lxml')
        # 获取content下的所有文本内容
        content = soup.find(id='content').text
        # 剔除广告
        str1 = '喜欢大明王侯请大家收藏:(www.au26.com)大明王侯笔趣阁备用站更新速度最快。 '
        str2 = '&&ahref=http:www.&&;起点中文网www.欢迎广大书友光临阅读,最新、最快、最火的连载作品尽在起点原创!'
        pure_content = content.strip(str1).strip(str2)

        # 写入到txt中
        wf.write(chapter_name + '\n')
        wf.write(pure_content + '\n')
;