Python进阶多线程爬取网页项目实战
作者:Python进阶多线程爬取网页项目实战 发布时间:2021-01-03 23:16:14
上一篇文章介绍了并发和多线程的概念,这次就来向大家上一个实战来讲解一下如何真正的运用上多线程这个概念。
有需要的可以看看我之前这篇文章:Python进阶篇之多线程爬取网页
一、网页分析
这次我们选择爬取的网站是水木社区的Python页面
网页:https://www.mysmth.net/nForum/#!board/Python?p=1
根据惯例,我们第一步还是分析一下页面结构和翻页时的请求。
通过前三页的链接分析后得知,每一页链接中最后的参数是页数,我们修改它即可得到其他页面的数据。
再来分析一下,我们需要获取帖子的链接就在id 为 body 的 section下,然后一层一层找到里面的 table,我们就能遍历这些链接的标题。
我们点开一篇帖子:https://www.mysmth.net/nForum/#!article/Python/162717
和前面一样,我们先分析标题和内容在网页中的结构
不难发现,主题部分只要找到id 为 main 的 section 下面的 class 为 b-head corner 的下面第二个 span即可
主题部分
而内容部分只要找到class 为 a-wrap corner 的 div,找到下面的 a-content即可。
内容部分
分析网页结构后,我们就可以开始写代码了!
二、代码实现
首先要确定要保存什么内容:这次我们保存水木社区 Python 版面前 10 页的所有帖子标题和帖子第一页的所有回复。
解析列表页,得到所有的帖子链接
from bs4 import BeautifulSoup
# 解析列表页内容,得到这一页的内容链接
def parse_list_page(text):
soup = BeautifulSoup(text, 'html.parser')
# 下面相当于 soup.find('table', class_='board-list tiz').find('tbody')
tbody = soup.find('table', class_='board-list tiz').tbody
urls = []
for tr in tbody:
td = tr.find('td', class_='title_9')
urls.append(td.a.attrs['href'])
return urls
解析内容页,得到标题和这一页的所有帖子内容
# 解析内容页,得到标题和所有帖子内容
def parse_content_page(text):
soup = BeautifulSoup(text, 'html.parser')
title = soup.find('span', class_='n-left').text.strip('文章主题:').strip()
content_div = soup.find('div', class_='b-content corner')
contents = []
for awrap in content_div.find_all('div', class_='a-wrap corner'):
content = awrap.p.text
contents.append(content)
return title, contents
把列表页的链接转换成我们要抓取的链接
def convert_content_url(path):
URL_PREFIX = 'http://www.mysmth.net'
path += '?ajax'
return URL_PREFIX + path
生成前 10 页的列表页链接
list_urls = []
for i in range(1, 11):
url = 'http://www.mysmth.net/nForum/board/Python?ajax&p='
url += str(i)
list_urls.append(url)
下面是得到前 10 页列表页里所有正文的链接。这个时候我们使用线程池的方式来运行
import requests
from concurrent import futures
session = requests.Session()
executor = futures.ThreadPoolExecutor(max_workers=5)
# 这个函数获取列表页数据,解析出链接,并转换成真实链接
def get_content_urls(list_url):
res = session.get(list_url)
content_urls = parse_list_page(res.text)
real_content_urls = []
for url in content_urls:
url = convert_content_url(url)
real_content_urls.append(url)
return real_content_urls
# 根据刚刚生成的十个列表页链接,开始提交任务
fs = []
for list_url in list_urls:
fs.append(executor.submit(get_content_urls, list_url))
futures.wait(fs)
content_urls = set()
for f in fs:
for url in f.result():
content_urls.add(url)
在这里要注意一下,第 23 行中我们使用了 set() 函数,作用是去除重复值。它的原理是创建一个 Set(集合),集合 是 Python 中的一种特殊数据类型,其中可以包含多个元素,但是不能重复。我们来看看 set() 的用法
numbers = [1, 1, 2, 2, 2, 3, 4]
unique = set(numbers)
print(type(unique))
# 输出:<class 'set'>
print(unique)
# 输出:{1, 2, 3, 4}
我们看到,set() 将列表 numbers 转换成了没有重复元素的集合 {1, 2, 3, 4}。
我们利用这个特性,首先在 23 行通过 content_urls = set() 创建了一个空集合,之后在其中添加链接时,就会自动去除多次出现的链接。
得到了所有正文链接之后,我们解析正文页内容,放到一个字典里
# 获取正文页内容,解析出标题和帖子
def get_content(url):
r = session.get(url)
title, contents = parse_content_page(r.text)
return title, contents
# 提交解析正文的任务
fs = []
for url in content_urls:
fs.append(executor.submit(get_content, url))
futures.wait(fs)
results = {}
for f in fs:
title, contents = f.result()
results[title] = contents
print(results.keys())
就这样,我们完成了多线程的水木社区爬虫。打印 results.keys() 可以看到所有帖子的标题。
这次爬取了前十页的所有主题,以及他们的第一页回复。一共 10 个列表页、300 个主题页,解析出 1533 条回复。在一台网络良好、性能普通的机器上测试执行只花费了 13 秒左右。
完整代码如下
import requests
from concurrent import futures
from bs4 import BeautifulSoup
# 解析列表页内容,得到这一页的内容链接
def parse_list_page(text):
soup = BeautifulSoup(text, 'html.parser')
tbody = soup.find('table', class_='board-list tiz').tbody
urls = []
for tr in tbody:
td = tr.find('td', class_='title_9')
urls.append(td.a.attrs['href'])
return urls
# 解析内容页,得到标题和所有帖子内容
def parse_content_page(text):
soup = BeautifulSoup(text, 'html.parser')
title = soup.find('span', class_='n-left').text.strip('文章主题:').strip()
content_div = soup.find('div', class_='b-content corner')
contents = []
for awrap in content_div.find_all('div', class_='a-wrap corner'):
content = awrap.p.text
contents.append(content)
return title, contents
# 把列表页得到的链接转换成我们要抓取的链接
def convert_content_url(path):
URL_PREFIX = 'http://www.mysmth.net'
path += '?ajax'
return URL_PREFIX + path
# 生成前十页的链接
list_urls = []
for i in range(1, 11):
url = 'http://www.mysmth.net/nForum/board/Python?ajax&p='
url += str(i)
list_urls.append(url)
session = requests.Session()
executor = futures.ThreadPoolExecutor(max_workers=5)
# 这个函数获取列表页数据,解析出链接,并转换成真实链接
def get_content_urls(list_url):
res = session.get(list_url)
content_urls = parse_list_page(res.text)
real_content_urls = []
for url in content_urls:
url = convert_content_url(url)
real_content_urls.append(url)
return real_content_urls
# 根据刚刚生成的十个列表页链接,开始提交任务
fs = []
for list_url in list_urls:
fs.append(executor.submit(get_content_urls, list_url))
futures.wait(fs)
content_urls = set()
for f in fs:
for url in f.result():
content_urls.add(url)
# 获取正文页内容,解析出标题和帖子
def get_content(url):
r = session.get(url)
title, contents = parse_content_page(r.text)
return title, contents
# 提交解析正文的任务
fs = []
for url in content_urls:
fs.append(executor.submit(get_content, url))
futures.wait(fs)
results = {}
for f in fs:
title, contents = f.result()
results[title] = contents
print(results.keys())
本次分享到此结束,谢谢大家阅读!!
有问题欢迎评论区留言!!
更多关于Python多线程爬取网页实战的资料请关注脚本之家其它相关文章!
来源:https://blog.csdn.net/huisoul/article/details/120815958


猜你喜欢
- 保存代码到文件:logger.pyimport osimport logbookfrom logbook.more import Color
- 1.从官网下载mysql-5.7.21-windowx64.zip mysql下载页面2.解压到合适的位置(E:\mysql) 这名字是我改
- 应用场景:有时需要测试插入数据库的记录来测试,所以就非常需要用到这些脚本。创建表:CREATE TABLE `tables_a` ( &nb
- 前言之前实践了下face++在线人脸识别版本,这回做一下离线版本。github 上面有关于face_recognition的相关资料,本人只
- Python提供了多个内置模块用于操作日期时间,像calendar,time,datetime。time模块我在之前的文章已经有所介绍,它提
- Twig是一款快速、安全、灵活的PHP模板引擎,它内置了许多filter和tags,并且支持模板继承,能让你用最简洁的代码来描述你的模板。他
- DBScan 算法解释说明DBScan 是密度基于空间聚类,它是一种基于密度的聚类算法,其与其他聚类算法(如K-Means)不同的是,它不需
- Application Name(应用程序名称):应用程序的名称。如果没有被指定的话,它的值为.NET SqlClient Data Pro
- 今天用scrapy爬取壁纸的时候(url:http://pic.netbian.com/4kmein...)絮叨了一些问题,记录下来,供后世
- 我们讲了requests的用法以及利用requests简单爬取、保存网页的方法,这节课我们主要讲urllib和requests的区别。1、获
- 1.之前的写法(不报错):data = cursor.fetchall()data_name = data[0]['task_typ
- Tag,直译标签,是一种由用户自定义的、用于描述信息 [1]的关键词。Tagging是用户为信息赋予Tag的行为。Delicious,Fli
- 该脚本是为了结合之前的编写的脚本,来实现数据的比对模块,实现数据的自动化!由于数据格式是定死的,该代码只做参考,有什么问题可以私信我!CSV
- IE(internet explorer)公司:微软(MicroSoft)布局引擎:Trident(也做MSHTML)注:解析渲染
- CTE(Common Table Expressions)是从SQL Server 2005以后版本才有的。指定的临时命名结果集,这些结果集
- k8s容器互联-flannel vxlan 原理篇容器系列文章容器系列视频vxlan 模式通信原理flannel 在为不同主机的pod分配i
- Flask框架介绍Flask诞生于2010年,是Armin ronacher用Python语言基于Werkzeug工具箱编写的轻量级Web开
- 在实验中需要自己构造单独的HTTP数据报文,而使用SOCK_STREAM进行发送数据包,需要进行完整的TCP交互。因此想使用原始套接字进行编
- 听歌识曲,顾名思义,用设备“听”歌曲,然后它要告诉你这是首什么歌。而且十之八九它还得把这首歌给你播放出来。这样的功能在QQ音乐等应用上早就出
- 本文实例讲述了Python 类,property属性(简化属性的操作),@property,property()用法。分享给大家供大家参考,