课程计划:Python 爬虫

image.png

前置知识

0.1 Python 基础:了解基本的 Python 语法和数据结构,如列表、字典和元组。
0.2 HTTP 协议基础:了解 HTTP 请求和响应,状态码以及常见的 HTTP 方法(GET、POST 等)。
0.3 HTML/CSS 基础:了解 HTML 和 CSS 的基本结构,以便于解析网页内容。
0.4 Python 网络库:安装和使用 requests 库进行基本的 HTTP 请求。
0.5 Python 解析库:安装和使用 BeautifulSoup 库解析 HTML 内容。

主课程

1.1 爬虫介绍:什么是爬虫,爬虫的基本原理和应用场景。
1.2 爬虫的基本架构:爬虫的基本组成部分,如 URL 管理器、下载器、解析器和数据存储。
1.3 使用 requests 库:如何使用 requests 库发送 HTTP 请求,处理响应。
1.4 使用 BeautifulSoup 库:如何使用 BeautifulSoup 库解析 HTML 内容,提取所需信息。
1.5 实战案例:实现一个简单的爬虫,抓取指定网站的内容。
1.6 处理动态网页:使用 Selenium 库处理 JavaScript 渲染的动态网页内容。
1.7 数据存储:将爬取的数据存储到 CSV、JSON 或数据库中。
1.8 遵守爬虫规范:了解 robots.txt 文件和用户代理的使用,避免爬虫被封禁。
1.9 多线程和异步爬虫:提升爬虫效率,使用多线程或异步方法实现高效爬虫。
1.10 常见问题和解决方案:处理反爬虫机制、IP 封禁等问题。

1.1 爬虫介绍

爬虫 是一种自动化程序,用于从互联网上抓取数据。爬虫的基本原理是模拟浏览器发送 HTTP 请求,获取网页内容,然后解析并提取所需的信息。爬虫的应用场景非常广泛,包括数据采集、信息检索和搜索引擎等。

关键概念:

  • 爬虫的作用:自动化数据收集,节省人力,提高效率。
  • 爬虫的结构:通常由 URL 管理器、下载器、解析器和数据存储四部分组成。
  • 合法性和道德规范:遵守网站的 robots.txt 文件和用户代理规范,避免给服务器带来负担。

实际应用:

  • 搜索引擎:如 Google 和 Bing 使用爬虫抓取和索引网页内容。
  • 数据采集:如抓取电商网站的商品信息、社交媒体的用户数据等。
  • 学术研究:如分析网络上的文本、图片、视频等数据。

示例:Google 搜索引擎的爬虫工作流程

  1. URL 提交:用户提交一个查询,Google 的爬虫会从其数据库中提取相关 URL。
  2. 抓取网页:爬虫访问这些 URL,下载网页内容。
  3. 解析内容:解析网页中的文本、链接等信息。
  4. 存储数据:将解析后的数据存储到 Google 的索引数据库中,供用户查询。

1.2 爬虫的基本架构

爬虫通常由四个主要部分组成:URL 管理器、下载器、解析器和数据存储。下面我们详细介绍每个部分:

1. URL 管理器

  • 作用:管理需要爬取的 URL 列表,防止重复爬取。
  • 实现:可以使用队列或集合数据结构。

2. 下载器

  • 作用:发送 HTTP 请求,获取网页内容。
  • 实现:常用 Python 的 requests 库来实现。

3. 解析器

  • 作用:解析网页内容,提取需要的数据。
  • 实现:常用 BeautifulSoup 库来解析 HTML。

4. 数据存储

  • 作用:将提取的数据保存到本地文件或数据库中。
  • 实现:可以使用 CSV、JSON、SQL 数据库等。

示例:简单的爬虫架构


import requests
from bs4 import BeautifulSoup
import csv

# URL 管理器
start_url = 'http://example.com'
urls = [start_url]
visited_urls = set()

# 数据存储
csv_file = open('data.csv', 'w', newline='')
csv_writer = csv.writer(csv_file)
csv_writer.writerow(['Title', 'URL'])

while urls:
    url = urls.pop(0)
    if url in visited_urls:
        continue

    # 下载器
    response = requests.get(url)
    visited_urls.add(url)

    # 解析器
    soup = BeautifulSoup(response.text, 'html.parser')
    title = soup.find('title').text
    csv_writer.writerow([title, url])

    # 提取新的 URL
    for link in soup.find_all('a'):
        new_url = link.get('href')
        if new_url and new_url.startswith('http'):
            urls.append(new_url)

csv_file.close()

1.3 使用 requests

requests 库是一个非常强大且易于使用的 HTTP 库,用于发送 HTTP 请求。我们将介绍如何安装和使用 requests 库来发送基本的 HTTP 请求,并处理响应。

安装 requests

首先,你需要安装 requests 库。你可以在命令行中运行以下命令:

pip install requests

使用 requests 库发送 HTTP 请求

下面是一些基本的例子,展示如何使用 requests 库发送 GET 请求和 POST 请求。

发送 GET 请求

import requests

url = 'http://example.com'
response = requests.get(url)

# 检查响应状态码
print(response.status_code)

# 打印响应内容
print(response.text)

发送 POST 请求

import requests

url = 'http://example.com/post'
data = {'key': 'value'}
response = requests.post(url, data=data)

# 检查响应状态码
print(response.status_code)

# 打印响应内容
print(response.text)

常用的 HTTP 方法

  • GET: 从服务器请求数据(常用于获取网页内容)。
  • POST: 向服务器提交数据(常用于表单提交)。
  • PUT: 向服务器发送数据,替换指定资源。
  • DELETE: 从服务器删除指定资源。

处理响应

  • 响应状态码: response.status_code 用于检查请求是否成功(如 200 表示成功,404 表示未找到)。
  • 响应内容: response.text 用于获取响应的文本内容,response.json() 用于解析 JSON 响应。

示例:使用 requests 库抓取网页内容

import requests

url = 'http://example.com'
response = requests.get(url)

if response.status_code == 200:
    print('请求成功!')
    print(response.text)
else:
    print('请求失败,状态码:', response.status_code)

1.4 使用 BeautifulSoup

BeautifulSoup 是一个非常强大的 Python 库,用于解析 HTML 和 XML 文档。它提供了简单的方法来导航、搜索和修改解析树,使得从网页中提取数据变得容易。

安装 BeautifulSoup

首先,你需要安装 BeautifulSouplxml 解析器。你可以在命令行中运行以下命令:

pip install beautifulsoup4 lxml

使用 BeautifulSoup 库解析 HTML 内容

下面是一些基本的例子,展示如何使用 BeautifulSoup 库解析 HTML 内容,并提取所需的信息。

创建 BeautifulSoup 对象

from bs4 import BeautifulSoup

html_doc = """
<html>
<head>
    <title>示例网页</title>
</head>
<body>
    <h1>这是一个标题</h1>
    <p class="content">这是一些内容。</p>
    <a href="http://example.com">这是一个链接</a>
</body>
</html>
"""

soup = BeautifulSoup(html_doc, 'lxml')
print(soup.prettify())

查找元素

你可以使用 findfind_all 方法来查找元素。

查找第一个匹配的元素

title = soup.find('title')
print(title.text)
查找所有匹配的元素
paragraphs = soup.find_all('p')
for p in paragraphs:
    print(p.text)
查找具有特定属性的元素

link = soup.find('a', href='http://example.com')
print(link.text)

提取元素属性

你可以使用 attrs 属性来提取元素的属性。

link = soup.find('a')
print(link['href'])

示例:使用 BeautifulSoup 解析网页内容


import requests
from bs4 import BeautifulSoup

url = 'http://example.com'
response = requests.get(url)
soup = BeautifulSoup(response.text, 'lxml')

# 查找标题
title = soup.find('title').text
print('标题:', title)

# 查找所有段落
paragraphs = soup.find_all('p')
for p in paragraphs:
    print('段落:', p.text)

# 查找链接
link = soup.find('a')['href']
print('链接:', link)

1.5 实战案例:实现一个简单的爬虫

我们将结合之前学习的 requestsBeautifulSoup 库,来实现一个简单的爬虫,抓取指定网站的内容。

实战目标

实现一个爬虫,抓取某网站上的所有文章标题和链接,并将它们保存到一个 CSV 文件中。

步骤

  1. 使用 requests 库发送 HTTP 请求,获取网页内容。
  2. 使用 BeautifulSoup 库解析网页内容,提取文章标题和链接。
  3. 将提取的数据保存到 CSV 文件中。

实战代码示例

import requests
from bs4 import BeautifulSoup
import csv

# 目标网站的 URL
url = 'http://example.com/articles'

# 发送 HTTP 请求
response = requests.get(url)

# 检查请求是否成功
if response.status_code == 200:
    # 使用 BeautifulSoup 解析网页内容
    soup = BeautifulSoup(response.text, 'lxml')
    
    # 打开一个 CSV 文件,准备写入数据
    with open('articles.csv', 'w', newline='') as file:
        writer = csv.writer(file)
        writer.writerow(['Title', 'Link'])
        
        # 查找所有文章标题和链接
        articles = soup.find_all('article')
        for article in articles:
            title = article.find('h2').text
            link = article.find('a')['href']
            writer.writerow([title, link])
            
    print('数据已成功保存到 articles.csv 文件中。')
else:
    print('请求失败,状态码:', response.status_code)

代码详解

  1. 发送 HTTP 请求:使用 requests.get 发送请求,获取网页内容。
  2. 解析网页内容:使用 BeautifulSoup 解析网页内容,将 HTML 转换为一个可以方便操作的对象。
  3. 查找并提取数据:查找所有文章的标题和链接,并将它们提取出来。
  4. 保存数据:使用 Python 的 csv 模块将数据写入 CSV 文件。

1.6 处理动态网页

有些网页的内容是通过 JavaScript 动态加载的,普通的 requests 库无法抓取到这些内容。为了解决这个问题,我们可以使用 Selenium 库,它可以模拟用户操作浏览器,加载动态内容。

安装 Selenium

首先,你需要安装 Selenium 库和浏览器驱动程序(例如 ChromeDriver)。你可以在命令行中运行以下命令:

pip install selenium

下载并安装 ChromeDriver:https://sites.google.com/chromium.org/driver/

使用 Selenium 抓取动态网页内容

下面是一个使用 Selenium 抓取动态网页内容的基本示例。

示例:使用 Selenium 抓取动态网页内容

from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from bs4 import BeautifulSoup

# 配置 ChromeDriver 路径
chrome_driver_path = '/path/to/chromedriver'

# 初始化 Chrome 浏览器
chrome_options = Options()
chrome_options.add_argument('--headless')  # 设置无头模式
service = Service(chrome_driver_path)
driver = webdriver.Chrome(service=service, options=chrome_options)

# 目标网站的 URL
url = 'http://example.com/dynamic'

# 使用 Selenium 打开网页
driver.get(url)

# 等待动态内容加载完成
try:
    element_present = EC.presence_of_element_located((By.ID, 'dynamic-content'))
    WebDriverWait(driver, 10).until(element_present)
finally:
    # 获取网页内容
    html = driver.page_source
    driver.quit()

# 使用 BeautifulSoup 解析网页内容
soup = BeautifulSoup(html, 'lxml')

# 查找并提取动态加载的内容
dynamic_content = soup.find(id='dynamic-content')
print(dynamic_content.text)

代码详解

  1. 初始化 Chrome 浏览器:使用 Selenium 初始化一个 Chrome 浏览器实例,并配置为无头模式。
  2. 打开目标网站:使用 driver.get 打开目标网站。
  3. 等待动态内容加载:使用 WebDriverWait 等待特定元素加载完成。
  4. 获取网页内容:使用 driver.page_source 获取网页内容,并使用 BeautifulSoup 解析。

1.7 数据存储

在爬取并解析了网页内容后,下一步是将提取的数据存储起来。我们可以选择多种存储方式,包括 CSV 文件、JSON 文件和数据库(如 SQLite)。

存储到 CSV 文件

CSV(Comma-Separated Values)是一种常用的轻量级数据存储格式,适合存储结构化数据。

示例:存储数据到 CSV 文件
import csv

# 数据列表,每个元素是一个包含标题和链接的元组
data = [
    ('Title 1', 'http://example.com/1'),
    ('Title 2', 'http://example.com/2'),
]

# 打开一个 CSV 文件,准备写入数据
with open('data.csv', 'w', newline='') as file:
    writer = csv.writer(file)
    writer.writerow(['Title', 'Link'])  # 写入表头
    writer.writerows(data)  # 写入数据

存储到 JSON 文件

JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,适合存储嵌套和结构化的数据。

示例:存储数据到 JSON 文件
import json

# 数据列表,每个元素是一个包含标题和链接的字典
data = [
    {'title': 'Title 1', 'link': 'http://example.com/1'},
    {'title': 'Title 2', 'link': 'http://example.com/2'},
]

# 将数据存储到 JSON 文件
with open('data.json', 'w') as file:
    json.dump(data, file, indent=4)

存储到 SQLite 数据库

SQLite 是一个轻量级的嵌入式数据库,适合存储结构化数据。

示例:存储数据到 SQLite 数据库
import sqlite3

# 连接到 SQLite 数据库(如果数据库不存在,将会创建一个新的数据库)
conn = sqlite3.connect('data.db')
cursor = conn.cursor()

# 创建一个表
cursor.execute('''
CREATE TABLE IF NOT EXISTS articles (
    id INTEGER PRIMARY KEY,
    title TEXT,
    link TEXT
)
''')

# 数据列表,每个元素是一个包含标题和链接的元组
data = [
    ('Title 1', 'http://example.com/1'),
    ('Title 2', 'http://example.com/2'),
]

# 插入数据到表中
cursor.executemany('INSERT INTO articles (title, link) VALUES (?, ?)', data)

# 提交事务并关闭连接
conn.commit()
conn.close()

1.8 遵守爬虫规范

在编写和运行爬虫时,我们需要遵守一些规范和道德准则,以避免对目标网站造成负担或违反法律法规。这包括遵守 robots.txt 文件的规定、使用合适的用户代理、控制爬取速度等。

robots.txt 文件

robots.txt 是一个位于网站根目录的文本文件,用于指示爬虫哪些页面可以或不可以抓取。

示例:读取 robots.txt 文件
import requests

url = 'http://example.com/robots.txt'
response = requests.get(url)
print(response.text)

用户代理

用户代理字符串(User-Agent)用于标识请求是由哪种浏览器或爬虫发送的。通过设置合适的用户代理,可以避免被网站屏蔽。

示例:设置用户代理
import requests

url = 'http://example.com'
headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
}
response = requests.get(url, headers=headers)
print(response.status_code)

控制爬取速度

为了避免给目标网站造成过大负担,我们需要控制爬取速度。可以使用 time.sleep 函数在每次请求之间添加延迟。

示例:控制爬取速度
import time
import requests
from bs4 import BeautifulSoup

urls = ['http://example.com/page1', 'http://example.com/page2']

for url in urls:
    response = requests.get(url)
    soup = BeautifulSoup(response.text, 'lxml')
    print(soup.title.text)
    
    # 在每次请求之间添加延迟
    time.sleep(1)  # 延迟 1 秒

总结

  1. 遵守 robots.txt 文件:在抓取前检查并遵守网站的 robots.txt 文件规定。
  2. 使用合适的用户代理:设置合适的用户代理字符串,避免被屏蔽。
  3. 控制爬取速度:添加请求间隔,避免对目标网站造成负担。

1.9 多线程和异步爬虫

为了提升爬虫的效率,可以使用多线程或异步方法来同时处理多个请求。这有助于加快爬取速度,尤其是在处理大量网页时非常有用。

多线程爬虫

使用多线程可以并发地执行多个爬取任务,减少总的爬取时间。我们可以使用 concurrent.futures 模块来实现多线程爬虫。

示例:使用多线程实现爬虫
import concurrent.futures
import requests
from bs4 import BeautifulSoup

# 要爬取的 URL 列表
urls = [
    'http://example.com/page1',
    'http://example.com/page2',
    'http://example.com/page3'
]

# 爬取函数
def fetch_url(url):
    response = requests.get(url)
    soup = BeautifulSoup(response.text, 'lxml')
    return soup.title.text

# 使用 ThreadPoolExecutor 实现多线程
with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:
    results = list(executor.map(fetch_url, urls))

for result in results:
    print(result)

异步爬虫

使用异步编程可以在等待 I/O 操作时执行其他任务,从而提高效率。我们可以使用 aiohttpasyncio 库来实现异步爬虫。

示例:使用异步方法实现爬虫
import aiohttp
import asyncio
from bs4 import BeautifulSoup

# 要爬取的 URL 列表
urls = [
    'http://example.com/page1',
    'http://example.com/page2',
    'http://example.com/page3'
]

# 异步爬取函数
async def fetch_url(session, url):
    async with session.get(url) as response:
        text = await response.text()
        soup = BeautifulSoup(text, 'lxml')
        return soup.title.text

# 主异步函数
async def main():
    async with aiohttp.ClientSession() as session:
        tasks = [fetch_url(session, url) for url in urls]
        results = await asyncio.gather(*tasks)
        for result in results:
            print(result)

# 运行异步爬虫
asyncio.run(main())

代码详解

  1. 多线程爬虫:使用 concurrent.futures.ThreadPoolExecutor 创建线程池,并使用 executor.map 并发地执行爬取任务。
  2. 异步爬虫:使用 aiohttp 创建异步 HTTP 请求,使用 asyncio 管理异步任务,使用 await 等待 I/O 操作完成。

1.10 常见问题和解决方案

在编写和运行爬虫时,你可能会遇到一些常见的问题和挑战。以下是一些常见问题及其解决方案:

1. 反爬虫机制

许多网站会使用各种方法来检测并阻止爬虫。常见的反爬虫机制包括:

  • IP 封禁:当同一个 IP 地址在短时间内发送过多请求时,网站可能会封禁该 IP。
  • CAPTCHA:网站可能会要求用户输入 CAPTCHA 以验证是否为人类用户。
  • 动态内容加载:使用 JavaScript 动态加载内容,防止简单的 HTTP 请求抓取数据。
解决方案
  • 使用代理池:通过使用代理池来轮换 IP 地址,减少被封禁的风险。
  • 模拟人类行为:添加延迟、随机化请求间隔、设置合适的用户代理。
  • 处理 CAPTCHA:使用第三方服务或手动解决 CAPTCHA。
  • 使用 Selenium:处理动态加载内容。

2. 数据解析错误

有时,网页结构复杂或不一致,可能会导致解析错误。

解决方案
  • 检查网页结构:使用浏览器的开发者工具检查网页的 HTML 结构。
  • 使用 try-except 块:在解析代码中使用 try-except 块来捕获和处理解析错误。

3. 数据存储问题

存储大量数据可能会导致性能问题或数据丢失。

解决方案
  • 选择合适的存储方式:根据数据量和访问频率选择合适的存储方式,如 CSV、JSON 或数据库。
  • 定期备份:定期备份数据,防止数据丢失。

示例:使用代理池

import requests
from itertools import cycle

# 代理列表
proxies = [
    'http://proxy1.com',
    'http://proxy2.com',
    'http://proxy3.com'
]
proxy_pool = cycle(proxies)

url = 'http://example.com'

for i in range(10):
    proxy = next(proxy_pool)
    response = requests.get(url, proxies={'http': proxy, 'https': proxy})
    print(response.status_code)

示例:使用 try-except 块处理解析错误

import requests
from bs4 import BeautifulSoup

url = 'http://example.com'
response = requests.get(url)

try:
    soup = BeautifulSoup(response.text, 'lxml')
    title = soup.find('title').text
    print('标题:', title)
except AttributeError as e:
    print('解析错误:', e)
声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。