HOOOS

Python多线程爬虫实战:高效抓取网站文章并保存本地

0 3 爬虫小能手 Python爬虫多线程爬虫文章抓取
Apple

想快速批量下载某个网站的文章?用Python写个爬虫就能搞定!本文就来手把手教你如何用Python实现一个支持多线程的爬虫,可以高效地抓取指定网站上的所有文章,并保存到本地。不用担心,代码都有,直接抄作业就行!

1. 准备工作:安装必要的库

首先,确保你已经安装了Python环境。接下来,我们需要安装几个常用的库:

  • requests: 用于发送HTTP请求,获取网页内容。
  • BeautifulSoup4: 用于解析HTML文档,提取所需信息。
  • threading: 用于实现多线程。

你可以使用pip命令来安装这些库:

pip install requests beautifulsoup4

2. 爬虫的基本思路

一个简单的爬虫通常包含以下几个步骤:

  1. 发送请求: 使用requests库向目标网站发送HTTP请求,获取网页的HTML内容。
  2. 解析HTML: 使用BeautifulSoup4库解析HTML内容,找到包含文章链接的元素。
  3. 提取链接: 从找到的元素中提取文章链接。
  4. 下载文章: 遍历所有文章链接,分别下载每篇文章的内容。
  5. 保存文章: 将下载的文章内容保存到本地文件中。

3. 代码实现:单线程爬虫

我们先来实现一个简单的单线程爬虫,然后再逐步改进为多线程版本。

import requests
from bs4 import BeautifulSoup
import os


def download_article(url, save_dir):
    """下载并保存文章"""
    try:
        response = requests.get(url)
        response.raise_for_status()  # 检查请求是否成功
        soup = BeautifulSoup(response.text, 'html.parser')

        # 提取文章标题 (这里需要根据实际网站的HTML结构进行调整)
        title = soup.find('h1').text.strip() if soup.find('h1') else 'No Title'
        # 清理标题,移除不能用于文件名的字符
        title = ''.join(c if c.isalnum() or c in ' _-' else '' for c in title)

        # 提取文章内容 (这里也需要根据实际网站的HTML结构进行调整)
        content = soup.find('div', class_='article-content').text.strip() if soup.find('div', class_='article-content') else 'No Content'

        # 保存文章到本地文件
        filename = os.path.join(save_dir, f'{title}.txt')
        with open(filename, 'w', encoding='utf-8') as f:
            f.write(f'# {title}\n\n{content}')
        print(f'Downloaded: {title}')

    except requests.exceptions.RequestException as e:
        print(f'Error downloading {url}: {e}')
    except AttributeError as e:
        print(f'Error parsing {url}: {e}')
    except Exception as e:
        print(f'An unexpected error occurred: {e}')


def get_article_links(base_url, start_page, end_page):
    """获取指定范围页码的文章链接"""
    article_links = []
    for page_num in range(start_page, end_page + 1):
        url = f'{base_url}/page/{page_num}'  # 假设分页URL结构
        try:
            response = requests.get(url)
            response.raise_for_status()
            soup = BeautifulSoup(response.text, 'html.parser')

            # 提取文章链接 (这里需要根据实际网站的HTML结构进行调整)
            for link in soup.find_all('a', href=True): # 查找所有<a>标签
                href = link['href']
                if '/article/' in href:  # 假设文章链接包含'/article/'
                    article_links.append(href)

        except requests.exceptions.RequestException as e:
            print(f'Error fetching page {page_num}: {e}')
        except Exception as e:
            print(f'An unexpected error occurred while fetching page {page_num}: {e}')
    return article_links


if __name__ == '__main__':
    base_url = 'https://example.com'  # 替换为目标网站的URL
    start_page = 1
    end_page = 5
    save_dir = 'articles'  # 保存文章的目录

    if not os.path.exists(save_dir):
        os.makedirs(save_dir)

    article_links = get_article_links(base_url, start_page, end_page)

    for link in article_links:
        download_article(link, save_dir)

    print('All articles downloaded!')

代码解释:

  • download_article(url, save_dir)函数:负责下载单个文章并保存到本地。
    • 使用requests.get(url)获取文章内容。
    • 使用BeautifulSoup(response.text, 'html.parser')解析HTML。
    • 使用soup.find('h1').textsoup.find('div', class_='article-content').text提取文章标题和内容(注意:这里需要根据目标网站的HTML结构进行调整)。
    • 将文章保存到save_dir目录下,文件名为文章标题。
  • get_article_links(base_url, start_page, end_page)函数:负责获取指定页码范围内的所有文章链接。
    • 循环遍历start_pageend_page的所有页码。
    • 使用requests.get(url)获取每个页面的HTML内容。
    • 使用BeautifulSoup(response.text, 'html.parser')解析HTML。
    • 使用soup.find_all('a', href=True)找到所有<a>标签,然后筛选出包含文章链接的标签(注意:这里需要根据目标网站的HTML结构进行调整)。
  • if __name__ == '__main__'::主函数,负责设置目标网站URL、起始页码、结束页码、保存目录,然后调用get_article_links函数获取所有文章链接,最后循环遍历所有链接,调用download_article函数下载文章。

使用方法:

  1. base_url替换为你要抓取的网站的URL。
  2. 根据网站的分页规则,设置start_pageend_page
  3. 根据网站的HTML结构,修改download_article函数和get_article_links函数中提取文章标题、内容和链接的代码。
  4. 运行代码,即可开始抓取文章。

4. 升级:多线程爬虫

单线程爬虫速度比较慢,我们可以使用多线程来提高抓取效率。下面是多线程爬虫的代码:

import requests
from bs4 import BeautifulSoup
import os
import threading
import queue


def download_article(url, save_dir, q):
    """下载并保存文章,并从队列中移除任务"""
    try:
        response = requests.get(url, timeout=10)
        response.raise_for_status()  # 检查请求是否成功
        soup = BeautifulSoup(response.text, 'html.parser')

        # 提取文章标题 (这里需要根据实际网站的HTML结构进行调整)
        title = soup.find('h1').text.strip() if soup.find('h1') else 'No Title'
        # 清理标题,移除不能用于文件名的字符
        title = ''.join(c if c.isalnum() or c in ' _-' else '' for c in title)

        # 提取文章内容 (这里也需要根据实际网站的HTML结构进行调整)
        content = soup.find('div', class_='article-content').text.strip() if soup.find('div', class_='article-content') else 'No Content'

        # 保存文章到本地文件
        filename = os.path.join(save_dir, f'{title}.txt')
        with open(filename, 'w', encoding='utf-8') as f:
            f.write(f'# {title}\n\n{content}')
        print(f'Downloaded: {title}')

    except requests.exceptions.RequestException as e:
        print(f'Error downloading {url}: {e}')
    except AttributeError as e:
        print(f'Error parsing {url}: {e}')
    except Exception as e:
        print(f'An unexpected error occurred: {e}')
    finally:
        q.task_done()  # 标记任务完成


def get_article_links(base_url, start_page, end_page):
    """获取指定范围页码的文章链接"""
    article_links = []
    for page_num in range(start_page, end_page + 1):
        url = f'{base_url}/page/{page_num}'  # 假设分页URL结构
        try:
            response = requests.get(url, timeout=10)
            response.raise_for_status()
            soup = BeautifulSoup(response.text, 'html.parser')

            # 提取文章链接 (这里需要根据实际网站的HTML结构进行调整)
            for link in soup.find_all('a', href=True): # 查找所有<a>标签
                href = link['href']
                if '/article/' in href:  # 假设文章链接包含'/article/'
                    article_links.append(href)

        except requests.exceptions.RequestException as e:
            print(f'Error fetching page {page_num}: {e}')
        except Exception as e:
            print(f'An unexpected error occurred while fetching page {page_num}: {e}')
    return article_links


def worker(q, save_dir):
    """线程函数,从队列中获取任务并执行"""
    while True:
        url = q.get()
        download_article(url, save_dir, q)


if __name__ == '__main__':
    base_url = 'https://example.com'  # 替换为目标网站的URL
    start_page = 1
    end_page = 5
    save_dir = 'articles'  # 保存文章的目录
    num_threads = 10  # 线程数量

    if not os.path.exists(save_dir):
        os.makedirs(save_dir)

    article_links = get_article_links(base_url, start_page, end_page)

    # 创建队列
    q = queue.Queue()
    for link in article_links:
        q.put(link)

    # 创建并启动线程
    for i in range(num_threads):
        t = threading.Thread(target=worker, args=(q, save_dir))
        t.daemon = True  # 设置为守护线程
        t.start()

    # 等待所有任务完成
    q.join()

    print('All articles downloaded!')

代码解释:

  • import threading:导入threading库,用于实现多线程。
  • import queue:导入queue库,用于创建线程安全的队列。
  • download_article(url, save_dir, q)函数:修改后的download_article函数,接收一个队列q作为参数,并在下载完成后调用q.task_done(),表示任务完成。
  • worker(q, save_dir)函数:线程函数,从队列q中获取任务(文章链接),然后调用download_article函数下载文章。while True循环保证线程会一直从队列中获取任务,直到队列为空。
  • q = queue.Queue():创建一个线程安全的队列,用于存放文章链接。
  • for link in article_links: q.put(link):将所有文章链接放入队列中。
  • for i in range(num_threads): ...:创建并启动多个线程,每个线程都执行worker函数。
  • t.daemon = True:将线程设置为守护线程,当主线程退出时,守护线程也会自动退出。
  • q.join():等待队列中的所有任务完成。

使用方法:

  1. base_url替换为你要抓取的网站的URL。
  2. 根据网站的分页规则,设置start_pageend_page
  3. 根据网站的HTML结构,修改download_article函数和get_article_links函数中提取文章标题、内容和链接的代码。
  4. 设置num_threads,即线程数量,根据你的CPU核心数和网络状况进行调整。
  5. 运行代码,即可开始多线程抓取文章。

5. 注意事项

  • 遵守robots.txt协议: 在抓取网站之前,应该先查看网站的robots.txt文件,了解网站允许哪些爬虫抓取哪些内容。不要抓取网站禁止抓取的内容。
  • 设置合理的请求头: 为了避免被网站识别为爬虫,可以设置User-Agent请求头,模拟浏览器访问。
  • 控制抓取频率: 不要过于频繁地发送请求,以免给网站服务器造成压力。可以使用time.sleep()函数来控制抓取频率。
  • 处理异常: 在爬虫运行过程中,可能会遇到各种异常,例如网络连接错误、HTML解析错误等。应该使用try...except语句来捕获并处理这些异常,保证爬虫的稳定性。
  • 网站反爬虫机制: 很多网站都有反爬虫机制,例如验证码、IP限制等。如果遇到这些情况,需要采取相应的措施来应对,例如使用代理IP、识别验证码等。

6. 总结

本文介绍了如何使用Python实现一个支持多线程的爬虫,可以高效地抓取指定网站上的所有文章,并保存到本地。希望本文能够帮助你快速入门Python爬虫技术。记住,爬虫虽然强大,但也要遵守规则,文明爬取哦!

点评评价

captcha
健康