Python 网络数据采集(一):BeautifulSoup

 作者:高玉涵
 时间:2022.5.20 16:47
 博客:blog.csdn.net/cg_i

助力每一个不知死活的梦想。

起个头

 网络数据采集并不是一门语言的独门秘籍,Python、Java、PHP、C#、Go 等语言都可以讲出精彩的故事。有人说编程语言就是宗教,不同语言的设计哲学不同,行为方式各异,“非我族类,其心必异”,但本着美好生活、快乐修行的初衷,我对所有语言都时刻保持敬畏之心,尊重信仰自由,努力做好自己的功课。对爱好 Python 的人来说,人生苦短,Python 当歌!简洁轻松的语法,开箱即用的模块,强大快乐的社区,总可以快速构建出简单高效的解决方案。使用 Python 的日子总是充满快乐的,本人使用 Python 网络数据采集的故事也不例外。本着独乐乐,不如众乐乐,遂想不如将这次学习经历分享。鉴于网络数据采集涉及多个领域、内容包罗万象,就此一篇远不足以覆盖所有主题,与其如此,倒不如,做一个专栏,以一个个例子程序开始,通过思考和解决问题,一层层将主题展开。考虑到这是我的一次全新尝试,也只是利用工作之余闲暇时间,边学习边整理,能持否?就用一句话来自勉”但行好事,莫问前程“。

网络数据采集大有所为

 在大数据深入人心的时代,网络数据采集作为网络、数据库与机器学习等领域的交汇点,已经成为满足个性化网络数据需求的最佳实践。搜索引擎可以满足人们对数据的共性需求,即“我来了,我看见”,而网络数据采集技术可以进一步精练数据,把网络中杂乱无意的数据聚合成合理规范的形式,方便分析与挖掘,真正实现“我征服”。工作中,你可能经常为找数据而烦恼,或者眼睁睁看着眼前的几百页数据却只能长恨咫尺天涯,又或者数据杂乱无章的网站中满是带着陷阱的表单和坑爹的验证码,甚至需要的数据都在网页版的 PDF 和网络图片中。而作为一名网站管理员,你也需要了解常用的网络数据采集手段,以及常用的网络表单安全措施,以提高网站访问的安全性,所谓道高一尺,魔高一丈······一念清净,烈焰成池,一念觉醒,方登彼岸。

第一个任务

 一旦你开始网络数据采集,就会感受到浏览器为我们做的所有细节。网络上如果没有 HTML 文本格式层、CSS 样式层、JavaScript 执行层和图像渲染层,乍看起来会有点儿吓人,但是在这里,我将介绍如何不通过浏览器的帮助来格式化和理解数据。我的任务是爬取 https://free.kuaidaili.com/free/inha/ 这个页面里所有代理服务器的信息,思考之下通常的想法:

  • 通过网站域名获取 HTML数据
  • 根据目标信息解析数据
  • 存储目标信息
  • 如果有必要,移动到另一个网页重复这个过程。

 我将围绕上述思考来学习并编写爬虫程序。

1. Beautifulsoup 简介

 “美味的汤,绿色的浓汤,
 在热气腾腾的盖碗里装!
 谁不愿意尝一尝,这样的好汤?
 晚餐用的汤,美味的汤!”

 BeautifulSoup 库的名字取自刘易斯·卡罗尔在《爱丽丝梦游仙境》里的同名诗歌。
爱丽丝梦游仙境
以下内容引用自《Beautiful Soup 4.4.0 文档》

 Beautiful Soup 是一个可以从 HTML 或 XML 文件中提取数据的 Python 库.它能够通过你喜欢的转换器实现惯用的文档导航,查找,修改文档的方式. Beautiful Soup 会帮你节省数小时甚至数天的工作时间.
 就像它在仙境中的说法一样,BeautifulSoup 尝试化平淡为神奇。它通过定位 HTML 标签来格式化和组织复杂的网络信息,用简单易用的 Python 对象为我们展现 XML 结构信息。
1.1 安装 BeautifulSoup

 由于 BeautifulSoup 库不是 Python 标准库,因此需要单独安装。我使用最新的 BeautifulSoup 4 版本(也叫 BS4)。BeautifulSoup 4 的所有安装方法都可在官方文档里面找到,这里就不再过多赘述。另外,注意所有例子只在 Python 3.x 下运行。

1.1.2 运行 BeautifulSoup

 BeautifulSoup 库最常用的对象恰好就是 BeautifulSoup 对象。

from urllib.request import urlopen
from bs4 import BeautifulSoup
html = urlopen("http://www.baidu.com")
bsObj = BeautifulSoup(html.read())
print(bsObj.title)

 输出结果是:

<title>百度一下,你就知道</title>

 代码第一行的含义:它查找 Python 的 request 模块(在 urllib 库里面),只导入一个 urlopen 函数。urllib 是 python 的标准库(就是说你不用额外安装就可以运行这个例子),包含了从网络请求数据,处理 cookie,甚至改变请求头和用户代理这些元数据的函数。网络采集中将广泛使用 urllib,所以建议你读读这个库的 Python 文档(https://docs.python.org/3/library/urllib.html)。

 urlopen 用来打开并读取一个从网络获取的远程对象。因为它是一个非常通用的库(它可以轻松读取 HTML 文件、图像文件,或其他任何文件流),所以我将频繁地使用它。然后调用 html.read() 获取网页中的 HTML 内容。这样就可以把 HTML 内容传到 BeautifulSoup 对象,转换成下面的结构:

html -> ……

---- head -> 百度一下,你就知道

-------- title -> 百度一下,你就知道

 可以看出,我们从网页中提取的 <title>标签被嵌在 BeautifulSoup 对象 bsObj 结构的第二层(html -> head -> title)。但是,当我从对象里提取 title 标签的时候,可以直接调用它:

bsObj.title

 其实,下面的所有函数调用都可以产生同样的结果:

bsObj.html.head.title
bsObj.head.title
bsObj.html.title

 希望这个例子可以向你展示 BeautifulSoup 库的强大与简单。其实,任何 HTML(或 XML)文件的任意节点信息都可以被提取出来,只要目标信息的旁边或附近有标记就行。

1.1.3 复杂 HTML 解析

 当我们从复杂的网页中寻觅信息时,有很多技巧可以帮我们“剔去”网页上那些不需要的信息。从中抽取出我们需要的信息。下面我创建一个网络采集程序来抓取 https://free.kuaidaili.com/free/inha/ 这个网页。

 在这个页面里,所有免费代理信息内容都放在一个表格里,你可以看到网页源代码里的 table 标签,如下所示:

<table class="table table-bordered table-striped">
          <thead>
              <tr>
                <th>IP</th>
                <th>PORT</th>
                <th>匿名度</th>
                <th>类型</th>
                <th>位置</th>
                <th>响应速度</th>
                <th>最后验证时间</th>
              </tr>
            </thead>
            <tbody>
                <tr>
                    <td data-title="IP">103.37.141.69</td>
                    <td data-title="PORT">80</td>
                    <td data-title="匿名度">高匿名</td>
                    <td data-title="类型">HTTP</td>
                    <td data-title="位置">中国 北京  </td>
                    <td data-title="响应速度">6秒</td>
                    <td data-title="最后验证时间">2022-05-20 14:31:01</td>
                </tr>
                <tr>
                    <td data-title="IP">103.37.141.69</td>
                    <td data-title="PORT">80</td>
                    <td data-title="匿名度">高匿名</td>
                    <td data-title="类型">HTTP</td>
                    <td data-title="位置">中国 北京  </td>
                    <td data-title="响应速度">6秒</td>
                    <td data-title="最后验证时间">2022-05-20 13:31:01</td>
                </tr>
                <tr>
                    <td data-title="IP">103.37.141.69</td>
                    <td data-title="PORT">80</td>
                    <td data-title="匿名度">高匿名</td>
                    <td data-title="类型">HTTP</td>
                    <td data-title="位置">中国 北京  </td>
                    <td data-title="响应速度">6秒</td>
                    <td data-title="最后验证时间">2022-05-20 12:31:01</td>
                </tr>
                <tr>
                    <td data-title="IP">122.9.101.6</td>
                    <td data-title="PORT">8888</td>
                    <td data-title="匿名度">高匿名</td>
                    <td data-title="类型">HTTP</td>
                    <td data-title="位置">中国 香港 666666999999999.com </td>
                    <td data-title="响应速度">0.4秒</td>
                    <td data-title="最后验证时间">2022-05-20 11:31:01</td>
                </tr>
                <tr>
                    <td data-title="IP">106.15.197.250</td>
                    <td data-title="PORT">8001</td>
                    <td data-title="匿名度">高匿名</td>
                    <td data-title="类型">HTTP</td>
                    <td data-title="位置">中国 上海  联通</td>
                    <td data-title="响应速度">1秒</td>
                    <td data-title="最后验证时间">2022-05-20 10:31:02</td>
                </tr>
                <tr>
                    <td data-title="IP">14.215.212.37</td>
                    <td data-title="PORT">9168</td>
                    <td data-title="匿名度">高匿名</td>
                    <td data-title="类型">HTTP</td>
                    <td data-title="位置">中国 广东 东莞 电信</td>
                    <td data-title="响应速度">4秒</td>
                    <td data-title="最后验证时间">2022-05-20 09:31:01</td>
                </tr>
                <tr>
                    <td data-title="IP">103.37.141.69</td>
                    <td data-title="PORT">80</td>
                    <td data-title="匿名度">高匿名</td>
                    <td data-title="类型">HTTP</td>
                    <td data-title="位置">中国 北京  </td>
                    <td data-title="响应速度">6秒</td>
                    <td data-title="最后验证时间">2022-05-20 08:31:01</td>
                </tr>
                <tr>
                    <td data-title="IP">39.106.71.115</td>
                    <td data-title="PORT">7890</td>
                    <td data-title="匿名度">高匿名</td>
                    <td data-title="类型">HTTP</td>
                    <td data-title="位置">中国 北京  联通</td>
                    <td data-title="响应速度">0.3秒</td>
                    <td data-title="最后验证时间">2022-05-20 07:31:01</td>
                </tr>
                <tr>
                    <td data-title="IP">183.236.232.160</td>
                    <td data-title="PORT">8080</td>
                    <td data-title="匿名度">高匿名</td>
                    <td data-title="类型">HTTP</td>
                    <td data-title="位置">中国 广东 江门 移动</td>
                    <td data-title="响应速度">6秒</td>
                    <td data-title="最后验证时间">2022-05-20 06:31:01</td>
                </tr>
                <tr>
                    <td data-title="IP">202.55.5.209</td>
                    <td data-title="PORT">8090</td>
                    <td data-title="匿名度">高匿名</td>
                    <td data-title="类型">HTTP</td>
                    <td data-title="位置">中国 香港 电信 </td>
                    <td data-title="响应速度">0.3秒</td>
                    <td data-title="最后验证时间">2022-05-20 05:31:01</td>
                </tr>
                <tr>
                    <td data-title="IP">118.180.166.195</td>
                    <td data-title="PORT">8060</td>
                    <td data-title="匿名度">高匿名</td>
                    <td data-title="类型">HTTP</td>
                    <td data-title="位置">甘肃省庆阳市  电信</td>
                    <td data-title="响应速度">0.4秒</td>
                    <td data-title="最后验证时间">2022-05-20 04:31:01</td>
                </tr>
                <tr>
                    <td data-title="IP">121.13.252.61</td>
                    <td data-title="PORT">41564</td>
                    <td data-title="匿名度">高匿名</td>
                    <td data-title="类型">HTTP</td>
                    <td data-title="位置">广东省东莞市  电信</td>
                    <td data-title="响应速度">2秒</td>
                    <td data-title="最后验证时间">2022-05-20 03:31:01</td>
                </tr>
                <tr>
                    <td data-title="IP">111.3.118.247</td>
                    <td data-title="PORT">30001</td>
                    <td data-title="匿名度">高匿名</td>
                    <td data-title="类型">HTTP</td>
                    <td data-title="位置">中国 浙江 台州 移动</td>
                    <td data-title="响应速度">0.2秒</td>
                    <td data-title="最后验证时间">2022-05-20 02:31:02</td>
                </tr>
                <tr>
                    <td data-title="IP">61.216.185.88</td>
                    <td data-title="PORT">60808</td>
                    <td data-title="匿名度">高匿名</td>
                    <td data-title="类型">HTTP</td>
                    <td data-title="位置">中国 台湾 屏东县 </td>
                    <td data-title="响应速度">0.7秒</td>
                    <td data-title="最后验证时间">2022-05-20 01:31:01</td>
                </tr>
                <tr>
                    <td data-title="IP">202.55.5.209</td>
                    <td data-title="PORT">8090</td>
                    <td data-title="匿名度">高匿名</td>
                    <td data-title="类型">HTTP</td>
                    <td data-title="位置">中国 香港 电信 </td>
                    <td data-title="响应速度">0.3秒</td>
                    <td data-title="最后验证时间">2022-05-20 00:31:01</td>
                </tr>
            </tbody>
        </table>

 我们可以抓出整个页面,然后创建一个 BeautifulSoup 对象,和一开始使用的程序类似:

from urllib.request import urlopen
from bs4 import BeautifulSoup
html = urlopen("https://free.kuaidaili.com/free/inha/")
bsObj = BeautifulSoup(html)

 通过 BeautifulSoup 对象,我们可以用 findAll 函数抽取只包含在 <table><tr><td data-title="IP"></td></tr></table>标签里的文字,这样就会得到 IP (findAll 是一个非常灵活的函数,后面会经常用到它):

nameList = bsObj.findAll("td", {"data-title":"IP"})
for name in nameList:
	print(name.get_text())

 输出结果:

103.37.141.69
103.37.141.69
103.37.141.69
122.9.101.6
106.15.197.250
14.215.212.37
103.37.141.69
39.106.71.115
183.236.232.160
202.55.5.209
118.180.166.195
121.13.252.61
111.3.118.247
61.216.185.88
202.55.5.209

 代码执行以后就会按照表格中的顺序显示所有的 IP。这是怎么实现的呢?之前,我调用 bsObj.tagName 只能获取页面中的第一个指定的标签。现在,我调用 bsObj.findAll(tagName, tgaAttributes) 可以获取页面中所有指定的标签,不再只是第一个了。

 获取 IP 列表之后,程序遍历列表中所有的名字,然后打印 name.get_text(),就可以把标签中的内容分开显示了。

1.1.4 BeautifulSoup 的 find() 和 findAll()

 BeautifulSoup 的 find() 和 findAll() 可能是最常用的两个函数。借助它们,你可以通过标签的不同属性轻松地过滤 HTML 页面,查找需要的标签组或单个标签。

 这两个函数非常相似,BeautifulSoup 文档里两者的定义是这样的:

findAll(tag, attributes, recursive, text, limit, keywords)
find(tag, attributes, recursive, text, keywords)

 很可能你会发现,自己在 95% 的时间里都只需要使用前两个参数:tag 和 attributes。但是,我们还是应该仔细地观察所有的参数。

 标签参数 tag 前面已经介绍过——你可以传一个标签的名称或多个标签名称组成的 Python 列表做标签参数。例如,下面的代码将返回一个包含 HTML 文档中所有标题标签的列表:

findAll({"h1","h2","h3","h4","h5","h6"})

 属性参数 attributes 是用一个 Python 字典封装一个标签的若干属性对应的属性值。例如,下面这个函数会返回 HTML 文档里红色与绿色两种颜色的 span 标签:

findAll("span", {"class":{"green", "red"}})

 递归参数 recursive 是一个布尔变量。你想抓取 HTML 文档标签结构里多少层的信息?如果 recursive 设置为 True,findAll 就会根据你的要求去查找标签参数的所有子标签,以及子标签的子标签。如果 recursive 设置为 False,findAll 就只查找文档的一级标签。findAll 默认是支持递归查找的(recursive 默认值是 True);一般情况下这个参数不需要设置,除非你真正了解自己需要哪些信息,而且抓取速度非常重要,那时你可以设置递归参数。

 文本参数 text 有点不同,它是用标签的文本内容去匹配,而不是用标签的属性。假如我们想查找前面网页中类型为“HTTP”的标签数量,我们可以把之前的 findAll 方法换成下面的代码:

nameList = bsObj.findAll(text="HTTP")
print(len(nameList))

 输出结果为“15”。

 范围限制参数 limit,显然只用于 findAll 方法。find 其实等价于 findAll 的 limit 等于 1 时的情形。如果你只对网页中获取的前 X 项结果感兴趣,就可以设置它。但是要注意,这个参数设置之后,获得的前几项结果是按照网页上的顺序排序的,未必是你想要的那前几项。

 还有一个关键词参数 keyword,可以让你选择那些具有指定属性的标签。例如:

allText = bsObj.findAll(id="text")
print(allText[0].get_text())

 虽然关键词参数 keyword 在一些场景中很有用,但是,它是 BeautifulSoup 在技术上做的一个冗余功能。任何用关键词参数能完成的任务,同样可以用下面将介绍的技术解决。例如,下面两行代码是完全一样的:

bsObj.findAll(id="text")
bsObj.findAll("", {"id":"text"})

 另外,用 keyword 偶乐会出现问题,尤其是在用 class 属性查找标签的时候,因为 class 是 Python 中受保护的关键字。也就是说,class 是 Python 语言的保留字,在 Python 程序里是不能当作变量或参数名使用的。假如你运行下面的代码,Python 就会因为你误用 class 保留字而产生一个语法错误:

bsObj.findAll(class="green")

 不过,你可以用 BeautifulSoup 提供的有点臃肿的方案,在 class 后面增加一个下划线:

bsObj.findAll(class_="green")

 另外,你也可以用属性参数把 class 用引号包起来:

bsObj.findAll("", {"class":"green"})

 最后回忆一下前面的内容,通过标签参数 tag 把标签列表传到 findAll() 里获取一列标签,其实就是一个“或”关系的过滤器(即,先择所有带标签1或标签2或标签3······的一列标签)。如果你的标签列表很长,就需要花很长时间才能写完。而关键词参数 keyword 可以让你增加一个“与”关系的过滤器来简化工作。

1.1.5 其它 BeautifulSoup 对象

 看到这里,你已经见过 BeautifulSoup 库里的两种对象了。

  • BeautifulSoup 对象

 前面代码示例中的 bsObj

  • 标签 Tag 对象

 BeautifulSoup 对象通过 find 和 findAll,或者直接调用子标签获取的一列对象或单个对象,就像:

bsObj.title

 但是,这个库还有另外两种对象,虽然不常用,却应该了解一下。

  • NavigableString 对象

 用来表示标签里的文字,不是标签(有些函数可以操作和生成 NavigableString 对象,而不是标签对象)。

  • Comment 对象

 用来查找 HTML 文档的注释标签。<!-- 像这样 -->

 这四个对象是目前你用 BeautifulSoup 库时会遇到的所有对象。

1.1.6 导航树

 findAll 函数通过标签的名称和属性来查找标签。但是如果你需要通过标签在文档中的位置来查找标签,该怎么办?这就是导航树(Navigating Trees)的作用。在前面,我们看过用单一方向进行 BeautifulSoup 标签树的导航:

bsObj.tag.subTag.anotherSubTag

 现在我们还用免费代理页面,演示 HTML 导航树的纵向和横向导航(为了简洁,只对表格进行操作)。

 这个 表格 <table>可以映射成一棵树,如下所示:

  • table

  —— thead

    —— tr

      —— th

  —— tbody

    —— tr

      —— th

        —— td

······ 其它表格行省略

  1. 处理子标签和其他后代标签

 和许多其他库一样,在 BeautifulSoup 库里,孩子(child)和后代(descendant)有显著的不同:和人类的家谱一样,子标签就是一个父标签的下一级,而后代标签是指一个父标签下面所有级别的标签。例如,tr 标签是 table 标签的子标签,而 thead、tr、th 和 tbody 标签都是 table 标签的后代标签(示例页面中就是如此)。所有的子标签都是后代标签,但不是所有的后代标签都是子标签。

 一般情况下,BeautifulSoup 函数总是处理当前标签的后代标签。例如,bsObj.body.h1 选择了 body 标签后代里的第一个 h1 标签,不会去找 body 外面的标签。

 类似地,bsObj.div.findAll(“img”) 会找出文档中第一个 div 标签,然后获取这个 div 后代里所有的 img 标签列表。

 如果你只想找出子标签,可以用 .children 标签:

values = bsObj.find("td",{"data-title":"IP"}).children
for v in values:
	print(v)

 输出结果:

60.170.204.30

 这段代码会打印表格中找到的第一个符合指定属性的值。因为 td 标签后面没有子标签和后代标签,下面代码输出的结果完全一样:

value = bsObj.find("td",{"data-title":"IP"})
print(value)
values = bsObj.find("td",{"data-title":"IP"}).descendants
for v in values:
	print(v)
  1. 处理兄弟标签

 BeautifulSoup 的 next_siblings() 函数可以让收集表格数据成为简单的事情,尤其是处理带标题行的表格:

values = bsObj.find("td",{"data-title":"IP"}).next_siblings
for v in values:
	print(v.get_text())

 输出结果:

8060
高匿名
HTTP
中国 安徽 蚌埠 电信
1秒
2022-05-20 16:31:02

 这段代码会打印表里的所有列值,第一列除外。为什么第一列被跳过了呢?有两个理由。首先,对象不能把自己作为兄弟标签。任何时候你获取一个标签的兄弟标签,都不会包含这个标签本身。其次,这个函数只调用后面的兄弟标签。例如,如果我们选择一组标签中位于中间位置的一个标签,然后用 next_siblings() 函数,那么它就只会返回在它后面兄弟标签。因此,选择标签行然后调用 next_siblings,可以选择表格中除了标题行以外的所有行。

让标签的选择更具体 如果我们选择 bsObj.table.tr 或直接就用 bsObj.tr 来获取表格中的第一行,上面的代码也可以获得正确的结果。但是,我还是采用更长的形式写了一行代码,这可以避免各种意外。即使页面上只有一个表格(或其它目标标签),只用标签也很容易丢失细节。另外,页面布局总是不断变化的。一个标签这次是在表格中第一行的位置,没准儿哪天就在第二行或第三行了。如果想让你的爬虫更稳定,最好还是让标签的选择更加具体。如果有属性,就利用标签的属性。

 和 next_siblings 一样,如果你很容易找到一组兄弟标签中的最后一个标签,那么 previous_siblings 函数也会很有用。当然,还有 next_sibling 和 previous_sibling 函数,与前面的作用类似,只是它们返回的是单个标签,而不是一组标签。

  1. 父标签处理

 在抓取网页的时候,查找父标签的需求比查找子标签和兄弟标签要少很多。通常情况下,如果以抓取网页内容为目的来观察 HTML 页面,我们都是从最上层标签开始的,然后思考如何定位我们想要的数据块所在的位置。但是,偶尔在特殊情况下你也会用到父标签查找函数,parent 和 parents。

2. 一个完整的例子
'''
	作者:高玉涵
	时间:2022.5.20 17:50
	说明:爬取代理信息第一版
'''
import socket
from urllib.request import urlopen
from urllib.error import HTTPError
from bs4 import BeautifulSoup


def getTable(url):
	try:
		html = urlopen(url)
	except HTTPError as e:
		return None
	except socket.error as e:
		return None
	try:
		bsObj = BeautifulSoup(html.read(), 'html.parser')
		table = bsObj.table
	except AttributeError as e:
		return None
	return table

def getAgentData(table):
	# 抓取到代理数据
	agent_data = []
	# 表头
	theads = []
	# 获取表头
	theads = getThead(table)

	try:
		# 获取所有行
		rows = table.findAll('tr')
	except AttributeError as e:
		print("TR 标签未找到!")
		return None
	else:
		for row in rows:
			# 存放代理信息
			agent = {}
			for t in theads:
				# 逐行查找与列对应的数据
				text = row.find('td', {'data-title':t})
				if text is not None:
					agent.setdefault(t, text.get_text())
			if len(agent) != 0:
				agent_data.append(agent)
		return agent_data
	

def getThead(table):
	# 存放获取的表头值
	theads = []
	
	try:
		# 遍历表格头子标签
		for h in table.thead.tr.children:
			# 提取标签内的值去掉前后空格
			text = h.get_text().replace(" ","")
			# 忽略不可见的换行符
			if text != '\n':
				theads.append(text)
	except AttributeError as e:
		print("TR 标签未找到!")
		return None
	else:
		return theads


if __name__ == '__main__':
	# 免费代理信息表格
	table = getTable('https://free.kuaidaili.com/free/inha/')
	
	if table == None:
		print('Table could not be found')
	else:
		# 获取代理信息
		agents = getAgentData(table)
		for a in agents:
			print(a)

 输出结果:

{'IP': '183.247.199.114', 'PORT': '30001', '匿名度': '高匿名', '类型': 'HTTP', '位置': '中国 浙江 台州 移动', '响应速度': '0.4秒', '最后验证时间': '2022-05-20 17:31:01'}
{'IP': '60.170.204.30', 'PORT': '8060', '匿名度': '高匿名', '类型': 'HTTP', '位置': '中国 安徽 蚌埠 电信', '响应速度': '1秒', '最后验证时间': '2022-05-20 16:31:02'}
{'IP': '14.215.212.37', 'PORT': '9168', '匿名度': '高匿名', '类型': 'HTTP', '位置': '中国 广东 东莞 电信', '响应速度': '4秒', '最后验证时间': '2022-05-20 15:31:01'}
{'IP': '103.37.141.69', 'PORT': '80', '匿名度': '高匿名', '类型': 'HTTP', '位置': '中国 北京  ', '响应速度': '6秒', '最后验证时间': '2022-05-20 14:31:01'}
{'IP': '103.37.141.69', 'PORT': '80', '匿名度': '高匿名', '类型': 'HTTP', '位置': '中国 北京  ', '响应速度': '6秒', '最后验证时间': '2022-05-20 13:31:01'}
{'IP': '103.37.141.69', 'PORT': '80', '匿名度': '高匿名', '类型': 'HTTP', '位置': '中国 北京  ', '响应速度': '6秒', '最后验证时间': '2022-05-20 12:31:01'}
{'IP': '122.9.101.6', 'PORT': '8888', '匿名度': '高匿名', '类型': 'HTTP', '位置': '中国 香港 666666999999999.com ', '响应速度': '0.4秒', '最后验证时间': '2022-05-20 11:31:01'}
{'IP': '106.15.197.250', 'PORT': '8001', '匿名度': '高匿名', '类型': 'HTTP', '位置': '中国 上海  联通', '响应速度': '1秒', '最后验证时间': '2022-05-20 10:31:02'}
{'IP': '14.215.212.37', 'PORT': '9168', '匿名度': '高匿名', '类型': 'HTTP', '位置': '中国 广东 东莞 电信', '响应速度': '4秒', '最后验证时间': '2022-05-20 09:31:01'}
{'IP': '103.37.141.69', 'PORT': '80', '匿名度': '高匿名', '类型': 'HTTP', '位置': '中国 北京  ', '响应速度': '6秒', '最后验证时间': '2022-05-20 08:31:01'}
{'IP': '39.106.71.115', 'PORT': '7890', '匿名度': '高匿名', '类型': 'HTTP', '位置': '中国 北京  联通', '响应速度': '0.3秒', '最后验证时间': '2022-05-20 07:31:01'}
{'IP': '183.236.232.160', 'PORT': '8080', '匿名度': '高匿名', '类型': 'HTTP', '位置': '中国 广东 江门 移动', '响应速度': '6秒', '最后验证时间': '2022-05-20 06:31:01'}
{'IP': '202.55.5.209', 'PORT': '8090', '匿名度': '高匿名', '类型': 'HTTP', '位置': '中国 香港 电信 ', '响应速度': '0.3秒', '最后验证时间': '2022-05-20 05:31:01'}
{'IP': '118.180.166.195', 'PORT': '8060', '匿名度': '高匿名', '类型': 'HTTP', '位置': '甘肃省庆阳市  电信', '响应速度': '0.4秒', '最后验证时间': '2022-05-20 04:31:01'}
{'IP': '121.13.252.61', 'PORT': '41564', '匿名度': '高匿名', '类型': 'HTTP', '位置': '广东省东莞市  电信', '响应速度': '2秒', '最后验证时间': '2022-05-20 03:31:01'}
3. 下一节,抓取所有网页

 我基本完成了开篇设定的任务目标,从结果看,程序只爬取了首页上的数据,而网站所提供的众多代理服务器数据,都分散在的其它页面里。任务列表里的“如果有必要,移动到另一个网页重复这个过程”并未实现。这个任务将留在下一节中给出。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值