如何实现Python进阶多线程爬取网页项目
这篇文章主要介绍"如何实现Python进阶多线程爬取网页项目",在日常操作中,相信很多人在如何实现Python进阶多线程爬取网页项目问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答"如何实现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 requestsfrom concurrent import futuressession = 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))# 输出: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] = contentsprint(results.keys())
就这样,我们完成了多线程的水木社区爬虫。打印 results.keys() 可以看到所有帖子的标题。
这次爬取了前十页的所有主题,以及他们的第一页回复。一共 10 个列表页、300 个主题页,解析出 1533 条回复。在一台网络良好、性能普通的机器上测试执行只花费了 13 秒左右。
完整代码如下
import requestsfrom concurrent import futuresfrom 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] = contentsprint(results.keys())
到此,关于"如何实现Python进阶多线程爬取网页项目"的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注网站,小编会继续努力为大家带来更多实用的文章!