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)

代码解释

  1. 导入模块和库:

    • json: 用于处理 JSON 数据格式
    • os: 提供操作文件和目录的功能
    • datetime: 用于记录时间
    • makedirs: 用于创建目录
    • exists: 用于检查目录是否存在
    • requests: 用于发送 HTTP 请求
    • logging: 用于记录程序运行日志
    • re: 用于使用正则表达式解析网页内容
    • urljoin: 用于拼接 URL
    • multiprocessing: 用于创建多进程
  2. 配置日志:

    • logging.basicConfig 设置日志的基本配置,包括日志级别为 INFO 和日志格式
  3. 定义基本信息:

    • BASE_URL: 网站的根地址
    • TOTAL_PAGE: 需要爬取的总页数
    • RESULTS_DIR: 存放爬取结果的目录
  4. 创建结果目录:

    • 使用 exists 检查 RESULTS_DIR 目录是否存在,如果不存在则使用 makedirs 创建
  5. 函数 scrape_page(url):

    • 爬取指定 URL 的网页内容
    • 记录日志信息
    • 使用 requests.get 发送 GET 请求
    • 判断响应状态码是否为 200,如果是则返回页面内容,否则记录错误日志
  6. 函数 scrape_index(page):

    • 爬取指定页码的索引页面
    • 构建索引页面的 URL
    • 调用 scrape_page 函数获取页面内容
  7. 函数 parse_index(html):

    • 解析索引页面的 HTML 内容,获取详情页的 URL
    • 使用正则表达式 re.compile 匹配详情页 URL
    • 使用 urljoin 拼接完整的 URL
    • 使用 yield 语句生成器返回 URL
  8. 函数 scrape_detail(url):

    • 爬取指定 URL 的详情页面
    • 调用 scrape_page 函数获取页面内容
  9. 函数 parse_detail(html):

    • 解析详情页面的 HTML 内容,提取所需数据
    • 使用正则表达式匹配封面、名称、分类、上映日期、剧情和评分等信息
    • 返回一个包含这些信息的字典
  10. 函数 save_data(data):

  • 将数据保存为 JSON 文件
  • 根据数据的名称构建文件路径
  • 使用 json.dump 将数据以 JSON 格式写入文件
  1. 函数 main(page):
  • 主函数,负责爬取单个页面并保存数据
  • 记录日志信息
  • 调用 scrape_index 函数获取索引页面的 HTML 内容
  • 调用 parse_index 函数解析 HTML 内容,获取详情页的 URL
  • 循环遍历详情页的 URL,依次调用 scrape_detailparse_detail 函数获取数据
  • 调用 save_data 函数将数据保存为 JSON 文件
  • 记录日志表示保存成功
  1. 多进程执行:
  • 使用 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 格式保存数据。代码涵盖了爬取页面、解析数据、保存数据等步骤,并通过多进程加速了爬取速度。 理解代码的编辑流程和运行逻辑对于学习网页爬取和多进程编程都是十分有益的。

Python 多进程爬虫代码分析:从编辑到运行流程

原文地址: https://www.cveoy.top/t/topic/o6sj 著作权归作者所有。请勿转载和采集!

免费AI点我,无需注册和登录