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


    1. 解析库beautifulsoup

    1.1 介绍

    BeautifulSoup是一个可以从HTML或XML文件中提取数据的Python库.
    官方文档: https://www.crummy.com/software/BeautifulSoup/bs4/doc/index.zh.html
    
    • 1
    • 2

    1.2 解析库

    Python2.7.3之前的版本和Python3中3.2.2之前的版本, 必须安装lxml或html5lib, 
    因为那些Python版本的标准库中内置的HTML解析方法不够稳定.
    
    • 1
    • 2
    解析器使用方法优势劣势
    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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    1.3 容错处理

    文档的容错能力: 指的是在html代码不完整的情况下, 使用该模块可以识别该错误。
    使用BeautifulSoup解析上述代码, 能够得到一个 BeautifulSoup 的对象, 并能按照标准的缩进格式的结构输出.
    
    • 1
    • 2
    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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    1.4 遍历文档树

    遍历文档树: 即直接通过标签名字选择, 特点是选择速度快, 但如果存在多个相同的标签则只返回第一个.
    * 多个相同标签只返回第一个, 第一个算遍历, 其他的都不算!
    
    • 1
    • 2
    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>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    2. 获取标签名称
    soup = BeautifulSoup(html_doc, 'lxml')  
    print(soup.body.p.name)  # p
    
    
    • 1
    • 2
    • 3
    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']
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    4. 获取标签的内容
    * 1. 文本内容
    
    • 1
    soup = BeautifulSoup(html_doc, 'lxml')
    print(soup.body.p.text)  # 标题
    print(soup.body.p.get_text()) 
    
    
    • 1
    • 2
    • 3
    • 4
    soup = BeautifulSoup(html_doc, 'lxml')
    # 获取第二个p标签, next_sibling下一个兄弟标签, 空行算一个next_sibling
    print(soup.body.p.next_sibling.next_sibling.text)
    """
    开始,
    A
    B 中间
    C
    结束.
    """
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    如果tag包含了多个子节点, tag就无法确定 
    .string 方法应该调用哪个子节点的内容, .string 的输出结果是 None.
    如果只有一个子节点那么就输出该子节点的文本,比如下面的这种结构, .string 返回为None,
    .strings就可以找到所有文本, 值是一个生成器.
    
    • 1
    • 2
    • 3
    • 4
    soup = BeautifulSoup(html_doc, 'lxml')
    
    # 第一个p标签
    print(soup.body.p.string)
    # 第二个p标签, 标签下文本只有一个, 没有其他标签时, 可以取到值, 否则为None
    print(soup.body.p.next_sibling.next_sibling.string)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    # 第一个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结束.']
    
    • 1
    • 2
    • 3
    • 4
    • 5
    soup = BeautifulSoup(html_doc, 'lxml')
    
    for line in soup.body.p.next_sibling.next_sibling.stripped_strings:  # 去掉空白
        print(line)
    """
    开始,
    A
    B
    中 间
    C
    结束.
    """
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    5. 嵌套选择
    soup = BeautifulSoup(html_doc, 'lxml')
    
    print(soup.head.title.text)  
    print(soup.body.a.text)
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    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)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    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标签
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    8. 兄弟节点
    * 空行被算成一个兄弟.
    
    • 1
    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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    1.5 搜索文档树

    1. 五种过滤器
    五种过滤器: 字符串, 正则表达式, 列表, True/False, 函数方法
    
    • 1
    * 1. 字符串过滤()
         find(): 找到一个就放回
         find_all(): 找多个
         ...
    
    • 1
    • 2
    • 3
    • 4
    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对象
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    * 2. 正则表达式
    
    • 1
    import re
    
    soup = BeautifulSoup(html_doc, 'lxml')
    re_t = re.compile('^t')
    print(soup.find(class_=re_t))  # <p class="title"><b>标题</b></p>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    * 3. 列表
    
    • 1
    soup = BeautifulSoup(html_doc, 'lxml')
    print(soup.find_all(class_=['title', 'sister']))
    
    
    • 1
    • 2
    • 3
    * 4. True/False
    
    • 1
    soup = BeautifulSoup(html_doc, 'lxml')
    # 存在或不存在否个属性
    print(soup.find(id=True))
    print(soup.p.find(id=False))
    print(soup.find(href=True))
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    * 5. 自定义函数
    
    • 1
    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))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    * 6. limit 参数与 recursive参数
       limit: 限制条数
       recursive: 当前层级查询不到, 递归查询(默认开启)
    
    • 1
    • 2
    • 3
    soup = BeautifulSoup(html_doc, 'lxml')
    
    print(soup.body.find_all(name='p', limit=1))
    
    
    • 1
    • 2
    • 3
    • 4
    soup = BeautifulSoup(html_doc, 'lxml')
    
    print(soup.body.find_all(name='b', recursive=False))
    
    
    • 1
    • 2
    • 3
    • 4
    2. css选择器
    select 返回的是列表形式.
    
    • 1
    soup = BeautifulSoup(html_doc, 'lxml')
    
    print(soup.select('#link1'))
    
    print(soup.select('body>p>b'))  
    
    print(soup.select('body p b'))
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    1.6 修改文档树

    * 1. 修改标签名称
    
    • 1
    soup = BeautifulSoup(html_doc, 'lxml')
    
    soup.body.p.name = 'h3'
    print(soup.body.h3.name)  # 将第一个p标签改为h3标签, html中没有h3会报错
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    * 2. 修改属性值
    
    • 1
    soup = BeautifulSoup(html_doc, 'lxml')
    
    soup.body.p['class'] = 'c1'
    print(soup.body.p.attrs)  # {'class': 'c1'}
    
    • 1
    • 2
    • 3
    • 4
    * 3. 修改值
    
    • 1
    soup = BeautifulSoup(html_doc, 'lxml')
    
    print(soup.body.p.string)  # 标题
    soup.body.p.string = 'xxx'
    print(soup.body.p.string)  # xxx
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    * 4. 添加值
    
    • 1
    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 
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    * 5. 删除标签
    
    • 1
    soup = BeautifulSoup(html_doc, 'lxml')
    print(soup.body.p)  # 展示第一个标签
    soup.body.p.decompose()   # 删除第一个p标签
    print(soup.body.p)  # 展示原第二标签
    
    
    • 1
    • 2
    • 3
    • 4
    • 5

    2. 宝塔面板与JumpServer堡垒机

    2.1 宝塔面板

    宝塔: 宝塔面板是一款支持windows和linux系统的服务器管理软件, 可通过Web端管理服务器, 提升运维效率. 
    使用宝塔前: 手工输入命令安装各类软件, 操作起来费时费力并且容易出错.
    而且需要记住很多Linux的命令,非常复杂.
    
    使用宝塔后: 2分钟装好面板, 一键管理服务器, 鼠标点几下就能替代以前的复杂繁多命令,
    操作简单, 看一眼就会使用. (图形化替代命令行操作)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    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的前端, 前端页面均由该项目提供, 主要负责页面后台的渲染.
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    3. 使用bs4库爬虫实例

    网站: https://www.autohome.com.cn/news/2/#liststart
    需要获取: 标题, 图片, 简介, 链接
    使用BeautifulSoup,从HTML文件中提取数据.
    
    • 1
    • 2
    • 3
    * 1. 分析网站
    
    • 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)
    
    • 1
    • 2
    • 3
    得到的ul信息中有文章也有广告.
    有h3标签的是文字, 没有h3标签的广告获取是空li标签.
    
    • 1
    • 2

    2022-06-16_123624

    广告
    
    • 1

    2022-06-16_130325

    空标签
    
    • 1

    2022-06-16_131900

    文章链接与封面路由
    
    • 1

    2022-06-16_133843

    * 2. 使用BeautifulSoup获取html的ul数据.
    
    • 1
    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)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    2022-06-16_123935

    * 3. 查询ul标签下所有的li标签
    
    • 1
    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)
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    2022-06-16_124931

    * 4. 获取标题, 图片, 简介, 链接的信息.
    
    • 1
    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}
    """
            )
    
    
    • 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

    2022-06-16_134043

    4. 创建代理池

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

    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)
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    * 2. 获取ip表单
    
    • 1
    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])
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    <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>
    
    • 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
    * 3. 获取表单中tr的标签信息
    
    • 1
    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)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    * table.children 获取table下说有的子标签, 行号算一个标签!!!
    正常的标签: <!--<class 'bs4.element.Tag'>-->
    空的标签: <!--<class 'bs4.element.NavigableString'>-->
    
    • 1
    • 2
    • 3
    <!--<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'>-->
    
    
    
    • 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
    * 4. 获取表单中tr的标签信息(剔除空行标签)
    
    • 1
    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)
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    * 5. 获取表单中的td标签
    
    • 1
    ...
    
    # 获取表单中的子节点, 遍历tr标签
    for tr in table.children:
        if isinstance(tr, bs4.element.Tag):
            # 遍历tr下的td标签
            for td in tr.children:
                print(td)
            print('----')
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    <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>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    * 6. 正则匹配获取ip与端口
    
    • 1
    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')]
    """
    
    • 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
    • 52
    • 53
    • 54
    • 55
    • 56

    5. 第三链接池项目

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

    2022-06-17_092729

    * 2. 解压并打来项目
    
    • 1
    * 3. 安装依赖: pip install -r requirements.txt
    
    • 1
    # 安装不上就换成国内源
    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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    * 4. 更新配置(配置好redis库即可)
    
    • 1
    # 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",
        # ....
    ]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    * 5. 启动项目
           如果已经具备运行条件, 可用通过proxyPool.py启动
           程序分为: schedule 调度程序  server Api服务
    
    • 1
    • 2
    • 3
    # 都在Terminal 中输入命令
    # 1. 启动调度程序(获取免费代理)
    python proxyPool.py schedule
    
    # 2. 启动webApi服务
    python proxyPool.py server
    启动报错:(更新 Flask Jinja2)
    pip uninstall  Flask Jinja2
    pip install Flask Jinja2
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    启动调度后获取免费代理
    
    • 1

    2022-06-17_100326

    * 6. 测试
    
    • 1

    2022-06-17_100148

    * 7. 随机获取一个请求
    
    • 1

    2022-06-17_100223

    * 8. 请求api介绍
      启动web服务后, 默认配置下会开启 http://127.0.0.1:5010 的api接口服务:
    
    • 1
    • 2
    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
    
    • 1
    * 9. 新建一个项目, 在项目中使用
    
    • 1
    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
    
    
    • 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

    6. 打码平台使用

    只演示图片验证码: 
    * 1. 将验证码下载到本地
    * 2. 将验证码上传到打码平台(将图片验证码给别人识别, 全国有几十台服务器, 有六千多工人, 24小时轮班...)
    * 3. 打码平台放回验证码
    验证码识别平台: http://www.chaojiying.com/
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    * 1. 注册一个账户 
    
    • 1
    * 2. 下载Demo实例代码包
    
    • 1

    2022-06-17_115724

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

    2022-06-17_120017

    * 4. 生成软件id
    
    • 1

    2022-06-17_121612

    2022-06-17_121718

    2022-06-17_121749

    * 5. 获取积分
    
    • 1

    2022-06-17_122239

    * 6. 打开项目测试
    
    • 1

    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代码
    
    
    • 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
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    * 7. 查看结果pic_str是验证码
    
    • 1

    image-20220617122444430

    7. 打码平台使用案例

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

    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)
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    * 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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    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': '1360012768@qq.com',
        '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}
    
    
    • 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

    8. bs4爬虫小说

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

    2022-06-19_124538

    2022-06-19_125157

    2022-06-19_125453

    2022-06-19_125800

    末尾还有两种广告!
    
    • 1

    2022-06-19_144007

    2022-06-19_144030

    * 2. 爬虫程序
    
    • 1
    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')
    
    • 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
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
  • 相关阅读:
    Portainer-docker可视化工具
    vue3 + vite + less配置全局style变量
    《web课程设计》用HTML CSS做一个简洁、漂亮的个人博客网站
    百模大战,打响AI应用生态的新赛点
    Hive数据定义语言DDL
    如何知道当前ubuntu的版本
    操作系统—死锁
    【NeurIPS && 图谱问答】知识图谱(KG) Mutil-Hop推理的锥形嵌入方法(中科院--含源码)
    java计算机毕业设计网上蛋糕订购系统源程序+mysql+系统+lw文档+远程调试
    了解.NET Framework中自带的泛型委托Predicate和Comparison
  • 原文地址:https://blog.csdn.net/qq_46137324/article/details/125496848