Python网络爬虫实战:爬取中国散文网青年散文专栏文章

一、引言

在当今数字时代,网络爬虫技术已成为获取和分析大规模在线数据的重要工具。本文将介绍一个实际的爬虫项目:爬取中国散文网青年散文专栏的所有文章。选择中国散文网作为爬取对象,是因为它是国内知名的散文平台,尤其是其青年散文专栏汇集了大量新生代作家的优秀作品,具有重要的文学价值和研究意义。

本项目的主要目标是获取青年散文专栏中的所有文章,并将其保存为txt格式的文本文件,便于后续的文本分析和研究。为了实现这一目标,我们将使用Python作为主要编程语言,结合Selenium WebDriver和BeautifulSoup库来进行网页爬取和解析。这种技术栈组合能够有效处理动态加载内容和复杂的HTML结构,从而确保爬取的全面性和准确性。

二、网站分析

中国散文网(essaychinacom)的整体结构相对清晰。网站主页包含多个专栏,其中青年散文专栏位于显著位置。

青年散文专栏的特点包括:

  1. 文章更新频繁,内容丰富多样。
  2. 每篇文章都有独立的详情页面。
  3. 作者信息和发布日期等元数据清晰可见。

分页机制分析显示,该专栏采用传统的静态分页方式。每页底部有明确的页码导航,通常每页显示15-20篇文章摘要。页面URL遵循规律,如"page/2/"表示第二页。

三、爬虫设计思路

本爬虫项目的总体架构采用模拟浏览器加HTML解析的方式。使用Selenium WebDriver模拟真实用户浏览行为,应对可能的动态加载内容;而BeautifulSoup则负责静态HTML的解析,提取所需的文章信息。

翻页策略方面,考虑到中国散文网采用静态分页,我们可以通过构造URL或点击"下一页"按钮来实现翻页。对于链接提取,DOM解析更适合本项目,因为文章链接通常位于固定的HTML结构中。

内容获取采用两步走策略:先定位到文章正文所在的HTML元素,然后进行文本清洗,去除广告、版权声明等无关内容。数据存储选择文件系统,将每篇文章保存为独立的txt文件,便于管理和后续处理。

为提高效率,我们将使用集合(set)来存储已爬取的链接,避免重复爬取。同时,实现一个简单的失败重试机制,以应对可能的网络波动。

四、核心技术原理

4.1 Selenium WebDriver

Selenium WebDriver的核心原理是通过驱动真实的浏览器(如Chrome、Edge等)来模拟用户操作。它能够执行JavaScript,渲染动态内容,这使得它特别适合爬取现代网页。

在本项目中,我们选择Edge WebDriver。配置时需要指定WebDriver的路径,并设置一些选项,如页面加载超时时间。例如:

edge_service = Service(executable_path='path/to/msedgedriver.exe')
driver = webdriver.Edge(service=edge_service)
driver.set_page_load_timeout(30)

这样设置可以确保在页面加载过慢时不会无限等待。

4.2 BeautifulSoup

BeautifulSoup是一个强大的HTML解析库。它将HTML文档转换成一个复杂的树形结构,每个节点都是Python对象。这些对象可以归纳为四种:Tag, NavigableString, BeautifulSoup, 和 Comment。

使用BeautifulSoup,我们可以轻松地通过标签名、类名、ID等来选择元素。例如:

soup = BeautifulSoup(html_content, 'html.parser')
article_links = soup.find_all('a', class_='article-link')

这行代码会找出所有类名为’article-link’的标签,这通常就是我们需要的文章链接。

4.3 异常处理机制

在网络爬虫中,异常处理至关重要。我们主要关注三类异常:

  1. 超时异常:设置页面加载时间限制,超时后尝试处理已加载的内容。
  2. 元素定位失败:使用显式等待(WebDriverWait)来等待元素出现。
  3. 网络错误:实现简单的重试机制,在失败后等待一段时间再重试。

4.4 URL处理

URL处理主要涉及两个方面:

  1. 相对路径转绝对路径:使用urllib.parse.urljoin()函数。

  2. URL编码和解码:使用urllib.parse.quote()和urllib.parse.unquote()函数,确保URL中的特殊字符被正确处理。

  3. 爬虫实现关键点

五、主要步骤

5.1 初始化配置

初始化时,我们需要设置WebDriver并获取用户输入。例如:

initial_url = input("请输入起始URL: ")
max_pages = int(input("请输入最大爬取页数: "))

5.2 翻页实现

翻页可以通过定位"下一页"按钮并点击实现。如果选择器会动态变化,可以使用更灵活的定位方式,如XPath。

5.3 链接提取

使用BeautifulSoup查找特定链接,并过滤掉不符合条件的链接。可以使用正则表达式或字符串方法来判断链接是否有效。

5.4 内容爬取

对于每个有效链接,我们打开页面,提取标题和正文。文本清洗是关键步骤,需要去除广告、版权声明等无关内容。

5.5 数据存储

创建输出目录,为每篇文章生成一个有效的文件名(去除非法字符),然后将清洗后的内容写入txt文件。

六、性能优化策略

为提高爬虫效率,我们采取以下策略:

  1. 使用集合(set)存储已爬取的链接,避免重复爬取。

  2. 采用动态等待而非固定等待,减少不必要的等待时间。

  3. 实现简单的失败重试机制,提高程序的鲁棒性。

  4. 考虑使用多线程或异步编程提高并发性,但需注意控制爬取速度,避免对目标网站造成过大压力。

为了避免被网站识别为爬虫并封禁,我们需要:

  1. 模拟人类行为,在请求之间添加随机等待时间。

  2. 轮换使用不同的User-Agent。

  3. 如果有必要,可以考虑使用IP代理池。

  4. 严格遵守网站的robots.txt规则,尊重网站管理员的爬虫政策。

  5. 结果分析

爬取完成后,我们需要评估爬虫的效率和数据质量:

  1. 爬取效率:记录总运行时间,计算平均每篇文章的爬取时间。

  2. 数据质量检查:随机抽查部分文章,确保内容完整性和准确性。

  3. 文本清洗效果:检查是否成功去除了所有无关内容,如广告和版权声明。

  4. 项目难点与解决方案

本项目面临的主要难点包括:

  1. 动态内容加载:使用Selenium WebDriver可以有效解决这个问题。

  2. 网页结构变化:采用更灵活的选择器,如XPath,并定期检查和更新爬虫代码。

  3. 大规模数据存储与管理:考虑使用数据库存储,如SQLite或MongoDB,便于后续数据分析。

  4. 法律和道德考虑

在进行网络爬虫项目时,我们必须考虑法律和道德问题:

  1. 版权问题:确保爬取的内容仅用于个人研究,不进行商业利用。

  2. 控制爬取频率:设置合理的爬取间隔,避免给目标网站服务器带来过大压力。

  3. 数据使用限制:遵守网站的使用条款,不滥用或未经授权传播爬取的内容。

  4. 总结与展望

本项目不仅实现了对中国散文网青年散文专栏的全面爬取,还提供了一个实用的网络爬虫开发框架。通过这个项目,我们深入理解了网络爬虫的工作原理和实现技巧。未来,我们可以考虑将这个爬虫扩展到其他文学网站,或者开发更高级的功能,如自动分类和内容分析。网络爬虫技术在数据收集和分析领域有着广阔的应用前景,值得我们继续探索和深入研究。

完整代码如下:

from selenium import webdriver
from selenium.webdriver.edge.service import Service
from selenium.common.exceptions import TimeoutException, NoSuchElementException
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from bs4 import BeautifulSoup
import os
import urllib.parse
import time

# 不需要的字符串列表
EXCLUDE_STRINGS = [
    "微信里点“发现”,扫一下",
    "二维码便可将本文分享至朋友圈。",
    # 如果还有其他需要排除的字符串,可以继续添加
]

# 指定Edge WebDriver的路径
edge_service = Service(executable_path='C:\\Users\\LENOVO\\Desktop\\爬虫\\实验\\msedgedriver.exe')

# 初始化Edge WebDriver
driver = webdriver.Edge(service=edge_service)

# 增加页面加载时间到30秒
driver.set_page_load_timeout(30)

# 使用input函数获取用户输入的网址
initial_url = input("请输入您想要打开的初始网址: ")

try:
    # 打开用户输入的网址
    driver.get(initial_url)
except TimeoutException:
    print("网页加载超时(30秒),将尝试爬取当前已加载的内容。")

# 用户输入的最大页数
max_pages = int(input("请输入要加载的最大页数: "))

# 创建一个目录来保存所有文件
file = input("请输入您要保存的文件夹名称: ")
output_dir = f"{file}"
os.makedirs(output_dir, exist_ok=True)

def clean_text(text):
    """
    移除文本中的特定字符串。
    :param text: 需要清理的文本
    :return: 清理后的文本
    """
    for string in EXCLUDE_STRINGS:
        text = text.replace(string, "")
    return text.strip()

# 用户输入的特定字段
specific_keyword = input("请输入您规定的URL字段: ")

# 定义翻页机制
current_page = 1
all_links = set()  # 存储所有链接

# 循环翻页并提取链接
while current_page <= max_pages:
    # 获取当前页面的HTML源代码
    page_source = driver.page_source
    soup = BeautifulSoup(page_source, 'html.parser')

    # 提取含有特定字段的所有链接
    links = [urllib.parse.urljoin(initial_url, link.get('href')) for link in soup.find_all('a', href=True) if specific_keyword in link.get('href', '')]

    valid_links = [link for link in links if urllib.parse.urlparse(link).scheme in ('http', 'https')]
    all_links.update(valid_links)
    print(f"在第 {current_page} 页找到 {len(valid_links)} 个有效的含有关键词 '{specific_keyword}' 的链接。")

    # 检查是否有下一页
    try:
        # 构造翻页按钮的选择器
        if current_page == 1:
            next_page_selector = "#aade1a9cdfa8099 > ul > li:nth-child(4) > a"
        else:
            next_page_selector = "#aade1a9cdfa8099 > ul > li:nth-child(5) > a"

        # 尝试重新查找元素并点击
        def find_and_click_next_page():
            try:
                # 每次都重新查找元素
                next_page_button = WebDriverWait(driver, 10).until(
                    EC.element_to_be_clickable((By.CSS_SELECTOR, next_page_selector))
                )
                time.sleep(1)  # 等待页面加载完成
                next_page_button.click()
                time.sleep(1)  # 等待页面加载完成
            except (TimeoutException, NoSuchElementException):
                # 如果找不到元素,则刷新页面并重试
                driver.refresh()
                WebDriverWait(driver, 10).until(EC.staleness_of(next_page_button))
                driver.get(initial_url)
                find_and_click_next_page()

        find_and_click_next_page()

    except (TimeoutException, NoSuchElementException):
        print(f"没有更多页面,结束翻页。")
        break

    current_page += 1

# 对于每个链接,执行相同的处理
processed_links = 0  # 已处理的链接数量
for url in all_links:
    try:
        # 打开链接
        driver.get(url)
    except TimeoutException:
        print(f"链接 {url} 加载超时,将尝试爬取当前已加载的内容。")
    except Exception as e:
        print(f"无法打开链接 {url}: {str(e)}")
        continue

    # 获取页面的HTML源代码
    time.sleep(1)  # 等待页面加载完成
    page_source = driver.page_source
    soup = BeautifulSoup(page_source, 'html.parser')

    # 获取网页的标题
    title = soup.title.string if soup.title else "default_title"

    # 处理文件名,去除非法字符
    filename = "".join(c if c.isalnum() or c.isspace() else '_' for c in title).rstrip().replace(" ", "_") + ".txt"
    filepath = os.path.join(output_dir, filename)

    # 查找所有的<p>标签
    time.sleep(1)  # 等待页面加载完成
    paragraphs = soup.find_all('p')

    # 同时打印到控制台和写入TXT文件
    with open(filepath, 'w', encoding='utf-8') as file:
        for p in paragraphs:
            text = p.get_text(strip=True)
            if text:  # 避免写入空行或打印空行
                cleaned_text = clean_text(text)  # 清理文本
                if cleaned_text:  # 避免写入空行或打印空行
                    print(cleaned_text)  # 打印到控制台
                    file.write(cleaned_text + "\n")  # 写入TXT文件

    processed_links += 1
    print(f"已处理 {processed_links}/{len(all_links)} 个链接。")

# 关闭浏览器
driver.quit()

这个代码运用的网站是青年散文_中国散文网 (cnprose.com),我使用的url字符标识是-liuyan。如果换到其他网站,翻页元素会有不同,按F12找到元素的selector复制进代码行一修改就能使用。不同网站的翻页元素会有不同,需要具体问题具体分析。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值