Python进阶多线程爬取网页项目实战

作者:Python进阶多线程爬取网页项目实战 时间:2021-01-03 23:16:14 

上一篇文章介绍了并发和多线程的概念,这次就来向大家上一个实战来讲解一下如何真正的运用上多线程这个概念。
有需要的可以看看我之前这篇文章:Python进阶篇之多线程爬取网页

一、网页分析

这次我们选择爬取的网站是水木社区的Python页面
网页:https://www.mysmth.net/nForum/#!board/Python?p=1

Python进阶多线程爬取网页项目实战

根据惯例,我们第一步还是分析一下页面结构和翻页时的请求。

Python进阶多线程爬取网页项目实战

Python进阶多线程爬取网页项目实战

Python进阶多线程爬取网页项目实战

通过前三页的链接分析后得知,每一页链接中最后的参数是页数,我们修改它即可得到其他页面的数据。

再来分析一下,我们需要获取帖子的链接就在id 为 body 的 section下,然后一层一层找到里面的 table,我们就能遍历这些链接的标题。

Python进阶多线程爬取网页项目实战

我们点开一篇帖子:https://www.mysmth.net/nForum/#!article/Python/162717

和前面一样,我们先分析标题和内容在网页中的结构

不难发现,主题部分只要找到id 为 main 的 section 下面的 class 为 b-head corner 的下面第二个 span即可
主题部分

Python进阶多线程爬取网页项目实战

而内容部分只要找到class 为 a-wrap corner 的 div,找到下面的 a-content即可。
内容部分

Python进阶多线程爬取网页项目实战

分析网页结构后,我们就可以开始写代码了!

二、代码实现

首先要确定要保存什么内容:这次我们保存水木社区 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

标签:Python,多线程,爬取网页,项目实战
0
投稿

猜你喜欢

  • Spark SQL常见4种数据源详解

    2024-01-18 09:07:21
  • 如何制作关联的下拉菜单?

    2010-06-29 21:19:00
  • python模拟哔哩哔哩滑块登入验证的实现

    2021-05-01 22:23:40
  • 详解springboot 使用c3p0数据库连接池的方法

    2024-01-19 04:59:09
  • Window环境下MySQL UDF提权

    2024-01-21 09:53:01
  • 手把手带你走进Go语言之常量解析

    2024-05-08 10:44:38
  • python中封包建立过程实例

    2021-11-13 14:29:08
  • 详解python函数传参是传值还是传引用

    2023-11-13 13:25:58
  • python3使用requests模块爬取页面内容的实战演练

    2022-01-08 18:26:57
  • python数据结构算法分析

    2022-06-11 02:57:15
  • Python使用BeautifulSoup库解析HTML基本使用教程

    2021-12-22 17:48:05
  • SQL Server 2005数据库还原错误的经典解决方案

    2024-01-21 00:54:56
  • JavaScript中利用各种循环进行遍历的方式总结

    2024-04-17 10:10:36
  • ASP 中 DateDiff 函数详解

    2007-09-19 12:00:00
  • 使用PyQt4 设置TextEdit背景的方法

    2021-09-01 14:41:43
  • sql函数:去掉html代码

    2008-04-07 12:44:00
  • sqlserver服务器验证改为混合验证模式步骤

    2024-01-20 18:02:28
  • Python简单实现自动删除目录下空文件夹的方法

    2021-07-28 20:28:39
  • Python操作word文档插入图片和表格的实例演示

    2023-09-20 08:21:09
  • Pytorch中的model.train() 和 model.eval() 原理与用法解析

    2022-06-06 20:51:04
  • asp之家 网络编程 m.aspxhome.com