Python 多进程爬虫代码分析:从编辑到运行流程
Python 多进程爬虫代码分析:从编辑到运行流程
本文将深入分析一段使用 Python 多进程实现网页爬取的代码,从代码编辑到运行流程,并画出流程图,帮助你理解其工作原理。
代码
import json,os,datetime
from os import makedirs
from os.path path import exists
import requests
import logging
import re
from urllib.parse import urljoin
import multiprocessing
logging.basicConfig(level=logging.INFO,
format='%(asctime)s - %(levelname)s: %(message)s')
BASE_URL = 'https://ssr1.scrape.center'
TOTAL_PAGE = 10
RESULTS_DIR = 'results'
exists(RESULTS_DIR) or makedirs(RESULTS_DIR)
def scrape_page(url):
logging.info('scraping ' + url + '...')
try:
response = requests.get(url)
if response.status_code == 200:
return response.text
logging.error(f'get invalid status code ' + response.status_code + ' while scraping ' + url)
except requests.RequestException:
logging.error(f'error occurred while scraping ' + url, exc_info=True)
def scrape_index(page):
index_url = f'{BASE_URL}/page/' + page
return scrape_page(index_url)
def parse_index(html):
pattern = re.compile('<a.*?href="(.*?)".*?class="name">')
items = re.findall(pattern, html)
if not items:
return []
for item in items:
detail_url = urljoin(BASE_URL, item)
logging.info(f'get detail url ' + detail_url)
yield detail_url
def scrape_detail(url):
return scrape_page(url)
def parse_detail(html):
cover_pattern = re.compile(
'class="item.*?<img.*?src="(.*?)".*?class="cover">', re.S)
name_pattern = re.compile('<h2.*?>(.*?)</h2>')
categories_pattern = re.compile(
'<button.*?category.*?<span>(.*?)</span>.*?</button>', re.S)
published_at_pattern = re.compile('(\d{4}-\d{2}-\d{2})\s?上映')
drama_pattern = re.compile('<div.*?drama.*?>.*?<p.*?>(.*?)</p>', re.S)
score_pattern = re.compile('<p.*?score.*?>(.*?)</p>', re.S)
cover = re.search(cover_pattern, html).group(
1).strip() if re.search(cover_pattern, html) else None
name = re.search(name_pattern, html).group(
1).strip() if re.search(name_pattern, html) else None
categories = re.findall(categories_pattern, html) if re.findall(
categories_pattern, html) else []
published_at = re.search(published_at_pattern, html).group(
1) if re.search(published_at_pattern, html) else None
drama = re.search(drama_pattern, html).group(
1).strip() if re.search(drama_pattern, html) else None
score = float(re.search(score_pattern, html).group(1).strip()
) if re.search(score_pattern, html) else None
return {
'cover': cover,
'name': name,
'categories': categories,
'published_at': published_at,
'drama': drama,
'score': score
}
def save_data(data):
name = data.get('name')
data_path = f'{RESULTS_DIR}/' + name + '.json'
json.dump(data, open(data_path, 'w', encoding='utf-8'),
ensure_ascii=False, indent=2)
def main(page):
logging.info(f'process id is ' + os.getpid() + ', to get page' + page)
index_html = scrape_index(page)
detail_urls = parse_index(index_html)
for detail_url in detail_urls:
detail_html = scrape_detail(detail_url)
data = parse_detail(detail_html)
logging.info(f'get detail data ' + data)
logging.info('saving data to json file')
save_data(data)
logging.info('data saved successfully')
if __name__ == '__main__':
start_time = datetime.datetime.now()
pool = multiprocessing.Pool()
pages = range(1, TOTAL_PAGE + 1)
pool.map(main, pages)
pool.close()
end_time = datetime.datetime.now()
print('用时: ',end_time-start_time)
代码解释
-
导入模块和库:
json: 用于处理 JSON 数据格式os: 提供操作文件和目录的功能datetime: 用于记录时间makedirs: 用于创建目录exists: 用于检查目录是否存在requests: 用于发送 HTTP 请求logging: 用于记录程序运行日志re: 用于使用正则表达式解析网页内容urljoin: 用于拼接 URLmultiprocessing: 用于创建多进程
-
配置日志:
logging.basicConfig设置日志的基本配置,包括日志级别为INFO和日志格式
-
定义基本信息:
BASE_URL: 网站的根地址TOTAL_PAGE: 需要爬取的总页数RESULTS_DIR: 存放爬取结果的目录
-
创建结果目录:
- 使用
exists检查RESULTS_DIR目录是否存在,如果不存在则使用makedirs创建
- 使用
-
函数
scrape_page(url):- 爬取指定 URL 的网页内容
- 记录日志信息
- 使用
requests.get发送 GET 请求 - 判断响应状态码是否为 200,如果是则返回页面内容,否则记录错误日志
-
函数
scrape_index(page):- 爬取指定页码的索引页面
- 构建索引页面的 URL
- 调用
scrape_page函数获取页面内容
-
函数
parse_index(html):- 解析索引页面的 HTML 内容,获取详情页的 URL
- 使用正则表达式
re.compile匹配详情页 URL - 使用
urljoin拼接完整的 URL - 使用
yield语句生成器返回 URL
-
函数
scrape_detail(url):- 爬取指定 URL 的详情页面
- 调用
scrape_page函数获取页面内容
-
函数
parse_detail(html):- 解析详情页面的 HTML 内容,提取所需数据
- 使用正则表达式匹配封面、名称、分类、上映日期、剧情和评分等信息
- 返回一个包含这些信息的字典
-
函数
save_data(data):
- 将数据保存为 JSON 文件
- 根据数据的名称构建文件路径
- 使用
json.dump将数据以 JSON 格式写入文件
- 函数
main(page):
- 主函数,负责爬取单个页面并保存数据
- 记录日志信息
- 调用
scrape_index函数获取索引页面的 HTML 内容 - 调用
parse_index函数解析 HTML 内容,获取详情页的 URL - 循环遍历详情页的 URL,依次调用
scrape_detail和parse_detail函数获取数据 - 调用
save_data函数将数据保存为 JSON 文件 - 记录日志表示保存成功
- 多进程执行:
- 使用
multiprocessing.Pool创建进程池 - 使用
pool.map并发执行main函数,每个进程负责爬取一个页面 - 关闭进程池
- 计算并打印程序运行时间
流程图
+------------------+
| |
| main() |
| |
+--------+---------+
|
v
+--------+---------+
| |
| scrape_index() |
| |
+--------+---------+
|
v
+--------+---------+
| |
| parse_index() |
| |
+--------+---------+
|
v
+--------+---------+
| |
| scrape_detail() |
| |
+--------+---------+
|
v
+--------+---------+
| |
| parse_detail() |
| |
+--------+---------+
|
v
+--------+---------+
| |
| save_data() |
| |
+--------+---------+
|
v
+--------+---------+
| |
| main() |
| (concurrent) |
| |
+--------+---------+
|
v
+--------+---------+
| |
| main() |
| (concurrent) |
| |
+--------+---------+
.
.
.
总结
这段代码展示了如何使用 Python 多进程实现网页爬取,并使用 JSON 格式保存数据。代码涵盖了爬取页面、解析数据、保存数据等步骤,并通过多进程加速了爬取速度。 理解代码的编辑流程和运行逻辑对于学习网页爬取和多进程编程都是十分有益的。
原文地址: https://www.cveoy.top/t/topic/o6sj 著作权归作者所有。请勿转载和采集!