Python 网络数据采集(三):采集整个网站

  • 1.采集整个网站
  • 2. 完整代码
  • 3. 下一节,继续优化这个“爬虫”程序
  • 参见

 作者:高玉涵
 时间:2022.5.30 15:35
 博客:blog.csdn.net/cg_i

心急吃不了热豆腐。

1.采集整个网站

 上一节中,实现了在示例网站以遍历方式从一个链接跳到另一个链接。但是,如果你需要要系统地把整个网站按目录分类,或者要搜索网站上的每一个页面,怎么办?那就得采集整个网站,那是一种非常耗费内存资源的过程,尤其是处理大型网站时,最合适的工具就是用一个数据库来储存采集的资源。但是,我暂时并不打算大规模地运行它们。关于数据库使用的相关知识,将放在后面适当的章节里讨论。

 一个常用的费时的网站采集方法就是从顶级页面开始(比如主页),然后搜索页面上的所有链接,形成列表。再去采集这些链接的每一个页面,然后把在每个页面上找到的链接形成新的列表,重复执行这一轮采集。

 为了避免一个页面被采集两次,链接去重是非常重要的。在代码运行时,把已发现的所有链接都放在一起,并保存在方便查询的列表里(下文示例指 Python 的集合 set 类型)。只有“新”链接才会被采集,之后再从页面中搜索其他链接:

from urllib.request import urlopen
from urllib.error import HTTPError
from bs4 import BeautifulSouppages = set()def getLinks(pageUrl):global pageshtml = urlopen(pageUrl)bsObj = BeautifulSoup(html.read(), 'html.parser')for link in bsObj.findAll('a'):if 'href' in link.attrs:if link.attrs['href'] not in pages:newPage = link.attrs['href']print(newPage)pages.add(newPage)getLinks(url)getLinks('https://free.kuaidaili.com')

 为了全面地展示这个网络数据采集示例是如何工作的,我降低了在前面例子里使用的“只寻找(/free/inha)路径“的标准。不再限制爬虫采集的页面范围,只要遇到页面就查找所有 <a>的标签。

 一开始,用 getLinks 处理主页。然后,遍历首页上每个链接,并检查是否已经在全局变量集合 pages 里面了(已经采集的页面集合)。如果不在,就打印到屏幕上,并把链接加入 pages 集合,再用 getLinks 递归地处理这个链接。

 输出结果:

/
/free/
/pricing/
/dps
/tps
/kps
/ops
/cart?p=day&t=dps&c=10&num=2500
/usercenter/dps/
/dps/fetch/
/dps/genapiurl/
/doc/dev/dps/#api
/doc/dev/dps/
/cart?p=month&t=tps&ct=dynamic
/usercenter/tps/
/tps/genapiurl/
/doc/dev/tps/#api
/doc/dev/tps/
/cart?p=month&t=kps_std&num=1
/usercenter/kps/
/kps/fetch/
/kps/genapiurl/
/doc/dev/kps/#api
/doc/dev/kps/
/cart?p=month&t=vip
/usercenter/ops/
/fetch/
/genapiurl/
/doc/api/getproxy/
/dist/
/helpcenter/
/doc/dev/quickstart/
/doc/dev/dps/#_3
/doc/dev/dps/#httpresponse
/doc/dev/dps/#_5
/doc/dev/sdk/
/doc/dev/sdk_http/
/doc/dev/tps/#_3
/doc/dev/tps/#httpresponse
/doc/dev/tps/#_5
/doc/dev/sdk_tps_http/
/doc/dev/kps/#_3
/doc/dev/kps/#httpresponse
/doc/dev/kps/#_5
/doc/dev/sdk_api_proxy/#_3
/doc/dev/ops/#_3
/doc/api/#2-api
/doc/dev/sdk_api_proxy/#_1
/doc/faq/buy/
/doc/faq/product/
/doc/faq/recharge/
/doc/faq/invoice/
javascript:void(0)
/support/
/entcustomer/
/changelog/
/tool/fetchua/
/cps/
/usercenter/
/login/
......(省略)

关于递归的警告

Python 默认的递归限制(程序递归地自我调用次数)是 1000 次。当程序达到递归限制后会停止(报错,RecursionError: maximum recursion depth exceeded in comparison),除非你设置一个较大的递归计数器,或者其他手段不让它停止。

解决办法:

可以将递归的深度修改的大一些,但还是不建议这样做。

import sys
sys.setrecursionlimit(100000) #例如这里设置为十万

2. 完整代码

'''作者:高玉涵时间:2022.5.27 13:20说明:抓取所有网页(爬虫版)
'''
import socket
import re
from tkinter.messagebox import NO
from types import coroutine
from typing import Counter
from urllib.request import urlopen
from urllib.parse import urljoin
from urllib.error import HTTPError
from bs4 import BeautifulSoupdef getTable(bsObj:BeautifulSoup):try:table = bsObj.tableexcept AttributeError as e:return Nonereturn tabledef getAgentData(table:BeautifulSoup):# 抓取到代理数据agent_data = []# 表头theads = []# 获取表头theads = getThead(table)try:# 获取所有行rows = table.findAll('tr')except AttributeError as e:print("TR 标签未找到!")return Noneelse: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_datadef getThead(table:BeautifulSoup):# 存放获取的表头值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 Noneelse:return theadsdef getUrl(url:str):'''获取 URL :param url 地址:return 返回 bs 对象'''try:html = urlopen(url, timeout=1)except HTTPError as e:return Noneexcept socket.error as e:return Nonetry:bsObj = BeautifulSoup(html.read(), 'html.parser')except AttributeError as e:return Noneexcept ValueError:return Nonereturn bsObjdef getMorePages(base_url:str, relative_url:str=""):'''获取更多页面:param base_url 基本 URL 地址:param relative_url 相对路径 URL:return None 失败'''global pages             # 采集过的页面global agent_list       # 保存采集到的代理数据global pages_error_count# 访问出错页面计数器agents = {}             # 代理结构# 拼接 URLurl = urljoin(base_url, relative_url)bsObj = getUrl(url)if bsObj == None:pages_error_count += 1 # 失败页面计数return Nonefor link in bsObj.findAll('a'):if 'href' in link.attrs:# 是新的链接if link.attrs['href'] not in pages:getMorePages(url, link.attrs['href'])# 打开链接指向的页面newUrl = urljoin(base_url, link.attrs['href'])newObj = getUrl(newUrl)if newObj == None:pages.add(link.attrs['href'])    # 保存处理过的页面pages_error_count += 1  # 失败页面计数continue                # 返回到循环开始处# 在打开的新页面里查找符合的数据table = getTable(newObj)if table == None:pages.add(link.attrs['href'])  # 保存处理过的页面pages_error_count += 1  # 失败页面计数continue                # 返回到循环开始处# 保存采集的数据agents = getAgentData(table)agent_list.extend(agents)pages.add(link.attrs['href'])    # 保存处理过的页面print(f"页面 {newUrl} 成功采集 {len(agent_list)} 条数据,忽略 {pages_error_count} 个页面。")if __name__ == '__main__':pages = set()            # 处理过的页面agent_list = []            # 保存采集到的代理数据pages_error_count = 0  # 访问出错页面计数器base_url = 'https://free.kuaidaili.com/free/inha'print(f"目标:{base_url}")user_choice = input('是否继续(y/n):')if user_choice not in('y','Y'): exit(0)getMorePages(base_url)

 输出结果:

目标:https://free.kuaidaili.com/free/inha
是否继续(y/n):y
页面 https://free.kuaidaili.com/free/ 成功采集 15 条数据,忽略 3 个页面。
页面 https://free.kuaidaili.com/free/inha/ 成功采集 30 条数据,忽略 154 个页面。
页面 https://free.kuaidaili.com/free/intr/ 成功采集 45 条数据,忽略 155 个页面。
页面 https://free.kuaidaili.com/free/inha/1/ 成功采集 60 条数据,忽略 158 个页面。
页面 https://free.kuaidaili.com/free/inha/2/ 成功采集 75 条数据,忽略 159 个页面。
页面 https://free.kuaidaili.com/free/inha/3/ 成功采集 90 条数据,忽略 160 个页面。
页面 https://free.kuaidaili.com/free/inha/4/ 成功采集 105 条数据,忽略 161 个页面。
页面 https://free.kuaidaili.com/free/inha/5/ 成功采集 120 条数据,忽略 162 个页面。
......(省略)

3. 下一节,继续优化这个“爬虫”程序

 这个 for 循环内调用递归的"爬虫"效率低的惊人且极易出错,你可能还发现偶而会丢失一点儿数据(或重复采集),下一节中我们将针对这些问题展开讨论。

参见

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

Python 网络数据采集(二):抓取所有网页

Python 网络数据采集(三):采集整个网站相关推荐

  1. python免费网络采集_python网络数据采集7 采集一个网站所有的外链

    [python]代码库from urllib.request import urlopen from bs4 import BeautifulSoup from urllib.parse import ...

  2. 笔记之Python网络数据采集

    笔记之Python网络数据采集 非原创即采集 一念清净, 烈焰成池, 一念觉醒, 方登彼岸 网络数据采集, 无非就是写一个自动化程序向网络服务器请求数据, 再对数据进行解析, 提取需要的信息 通常, ...

  3. python数据采集框架_20190715《Python网络数据采集》第 1 章

    <Python网络数据采集>7月8号-7月10号,这三天将该书精读一遍,脑海中有了一个爬虫大体框架后,对于后续学习将更加有全局感. 此前,曾试验看视频学习,但是一个视频基本2小时,全部拿下 ...

  4. 笨办法学Python(第四版)最新版+Python爬虫开发与项目实战+Python网络数据采集+精通Scrapy网络爬虫

    笨办法学Python(第四版)最新版+Python爬虫开发与项目实战+Python网络数据采集+精通Scrapy网络爬虫 本资料为最新整理高清带目录pdf,百度网盘下载~~~ 本资料为最新整理高清带目 ...

  5. Python网络数据采集2-wikipedia

    Python网络数据采集2-wikipedia 随机链接跳转 获取维基百科的词条超链接,并随机跳转.可能侧边栏和低栏会有其他链接.这不是我们想要的,所以定位到正文.正文在id为bodyContent的 ...

  6. Python 网络数据采集(中文版)

    Python 网络数据采集(中文版) 链接:https://pan.baidu.com/s/1vXtIUXVFlGLBnA3BbWFAAg 提取码:f2a8

  7. 《深入浅出Python》与《Python网络数据采集》读后感

    本学期在课程之外,自主计划学习Python语言以及爬虫知识.书籍方面,采用了O`Reilly的<深入浅出Python>与<Python网络数据采集>.前者用于Python语法的 ...

  8. python网络数据采集学习笔记-前言

    <Python网络数据采集>,英文名<web scraping with python>     Ryan Mitchell著     陶俊杰 陈小莉 译 那老僧道:" ...

  9. python网络数据爬取及分析_《Python网络数据采集》读后总结--第3章开始爬取数据及天善用户关系分析实例...

    这次介绍一下<Python网络数据采集>这本书的第3章内容(Chpt03.开始爬数据的内容), 使用了天善用户关系分析的示例来介绍一下具体实践. 1.第3章内容简介 1-getWikiLi ...

最新文章

  1. 树莓派安装samba共享文件
  2. 关于调试过程中单片机复位的几点心得与体会
  3. Win7下Anaconda3+Tensorflow
  4. if match header test - 428 Precondition required
  5. IPhone开发 用子类搞定不同的设备(iphone和ipad)
  6. Codeforces Round #143 (Div. 2) C
  7. java代码题目_java题目代码?
  8. Stack Overflow RToax
  9. markdown简明使用语法
  10. Zbrush 2022 for Mac(数字雕刻和绘画程序)
  11. java输入一个矩阵顺时针打印_剑指Offer(Java版):顺时针打印矩阵
  12. Reproxy:边缘服务器反向代理工具
  13. Anaconda如何更新pip
  14. 如何从网页上下载内嵌的PDF文件
  15. mvn命令及生命周期
  16. android 距离感应器控制屏幕熄灭_华为nova3e、小米6X、OPPOA7x对比,选IPS屏幕还是TFT屏幕,自选...
  17. Linux (deepin)网络管理详解
  18. 关于boa+cgi上传文件大小的问题
  19. [编译原理课设] 自动机的状态转换图表示
  20. MyBatis-plus从入门到精通(全)

热门文章

  1. Vim 输入法在normal和insert之间自动切换
  2. 2021年CVPR论文Deep Two-View Structure-from-Motion Revisited阅读笔记
  3. nginx之root alias proxy_pass测试
  4. A State-of-the-Art Survey on Deep Learning Theory and Architectures论文翻译分析
  5. 2099 找到和最大的长度为 K 的子序列
  6. composer 安装 thinkphp
  7. lmgrd遇到lmgrd: No such file or directory
  8. 仙剑游戏系列..感想
  9. 有一对兔子,从出生后的第 3 个月起每个月都生一对兔子。小兔子长到第 3 个月后每个月又生一对兔子,假设所有的兔子都不死,问 20 个月内每个月的兔子总数为多少?
  10. 关于signed main()不报超时与int main()报超时