当前位置: 代码网 > it编程>前端脚本>Python > Python爬虫技术 第14节 HTML结构解析

Python爬虫技术 第14节 HTML结构解析

2024年08月02日 Python 我要评论
HTML 结构解析是 Web 爬虫中的核心技能之一,它允许你从网页中提取所需的信息。Python 提供了几种流行的库来帮助进行 HTML 解析,其中最常用的是和lxml。

html 结构解析是 web 爬虫中的核心技能之一,它允许你从网页中提取所需的信息。python 提供了几种流行的库来帮助进行 html 解析,其中最常用的是 beautifulsouplxml

在这里插入图片描述

1. 安装必要的库

首先,你需要安装 requests(用于发送 http 请求)和 beautifulsoup4(用于解析 html)。可以通过 pip 安装:

pip install requests beautifulsoup4

2. 发送 http 请求并获取 html 内容

使用 requests 库可以轻松地从网站抓取 html 页面:

import requests

url = "https://www.example.com"
response = requests.get(url)

# 检查请求是否成功
if response.status_code == 200:
    html_content = response.text
else:
    print(f"failed to retrieve page, status code: {response.status_code}")

3. 解析 html 内容

接下来,使用 beautifulsoup 解析 html 内容:

from bs4 import beautifulsoup

soup = beautifulsoup(html_content, 'html.parser')

这里的 'html.parser' 是解析器的名字,beautifulsoup 支持多种解析器,包括 python 自带的标准库、lxmlhtml5lib

4. 选择和提取信息

一旦你有了 beautifulsoup 对象,你可以开始提取信息。以下是几种常见的选择器方法:

  • 通过标签名

    titles = soup.find_all('h1')
    
  • 通过类名

    articles = soup.find_all('div', class_='article')
    
  • 通过 id

    main_content = soup.find(id='main-content')
    
  • 通过属性

    links = soup.find_all('a', href=true)
    
  • 组合选择器

    article_titles = soup.select('div.article h2.title')
    

5. 遍历和处理数据

提取到数据后,你可以遍历并处理它们:

for title in soup.find_all('h2'):
    print(title.text.strip())

6. 递归解析

对于复杂的嵌套结构,你可以使用递归函数来解析:

def parse_section(section):
    title = section.find('h2')
    if title:
        print(title.text.strip())

    sub_sections = section.find_all('section')
    for sub_section in sub_sections:
        parse_section(sub_section)

sections = soup.find_all('section')
for section in sections:
    parse_section(section)

7. 实战示例

让我们创建一个完整的示例,抓取并解析一个简单的网页:

import requests
from bs4 import beautifulsoup

url = "https://www.example.com"

# 发送请求并解析 html
response = requests.get(url)
soup = beautifulsoup(response.text, 'html.parser')

# 找到所有的文章标题
article_titles = soup.find_all('h2', class_='article-title')

# 输出所有文章标题
for title in article_titles:
    print(title.text.strip())

这个示例展示了如何从网页中抓取所有具有 class="article-title"h2 元素,并打印出它们的文本内容。

以上就是使用 python 和 beautifulsoup 进行 html 结构解析的基本流程。当然,实际应用中你可能需要处理更复杂的逻辑,比如处理 javascript 渲染的内容或者分页等。

在我们已经讨论的基础上,让我们进一步扩展代码,以便处理更复杂的场景,比如分页、错误处理、日志记录以及数据持久化。我们将继续使用 requestsbeautifulsoup,并引入 loggingsqlite3 来记录日志和存储数据。

1. 异常处理和日志记录

在爬取过程中,可能会遇到各种问题,如网络错误、服务器错误或解析错误。使用 try...except 块和 logging 模块可以帮助我们更好地处理这些问题:

import logging
import requests
from bs4 import beautifulsoup

logging.basicconfig(filename='crawler.log', level=logging.info, format='%(asctime)s:%(levelname)s:%(message)s')

def fetch_data(url):
    try:
        response = requests.get(url)
        response.raise_for_status()  # raises an httperror for bad responses
        soup = beautifulsoup(response.text, 'html.parser')
        return soup
    except requests.exceptions.requestexception as e:
        logging.error(f"failed to fetch {url}: {e}")
        return none

# example usage
url = 'https://www.example.com'
soup = fetch_data(url)
if soup:
    # proceed with parsing...
else:
    logging.info("no data fetched, skipping...")

2. 分页处理

许多网站使用分页显示大量数据。你可以通过检查页面源码找到分页链接的模式,并编写代码来遍历所有页面:

def fetch_pages(base_url, page_suffix='page/'):
    current_page = 1
    while true:
        url = f"{base_url}{page_suffix}{current_page}"
        soup = fetch_data(url)
        if not soup:
            break
        # process page data here...

        # check for next page link
        next_page_link = soup.find('a', text='next')
        if not next_page_link:
            break
        current_page += 1

3. 数据持久化:sqlite

使用数据库存储爬取的数据可以方便后续分析和检索。sqlite 是一个轻量级的数据库,非常适合小型项目:

import sqlite3

def init_db():
    conn = sqlite3.connect('data.db')
    cursor = conn.cursor()
    cursor.execute('''
        create table if not exists articles (
            id integer primary key autoincrement,
            title text not null,
            author text,
            published_date date
        )
    ''')
    conn.commit()
    return conn

def save_article(conn, title, author, published_date):
    cursor = conn.cursor()
    cursor.execute('''
        insert into articles (title, author, published_date) values (?, ?, ?)
    ''', (title, author, published_date))
    conn.commit()

# initialize database
conn = init_db()

# save data
save_article(conn, "example title", "author name", "2024-07-24")

4. 完整示例:抓取分页数据并保存到 sqlite

让我们将上述概念整合成一个完整的示例,抓取分页数据并将其保存到 sqlite 数据库:

import logging
import requests
from bs4 import beautifulsoup
import sqlite3

logging.basicconfig(filename='crawler.log', level=logging.info)

def fetch_data(url):
    try:
        response = requests.get(url)
        response.raise_for_status()
        return beautifulsoup(response.text, 'html.parser')
    except requests.exceptions.requestexception as e:
        logging.error(f"failed to fetch {url}: {e}")
        return none

def fetch_pages(base_url, page_suffix='page/'):
    conn = sqlite3.connect('data.db')
    cursor = conn.cursor()
    cursor.execute('''
        create table if not exists articles (
            id integer primary key autoincrement,
            title text not null,
            author text,
            published_date date
        )
    ''')
    conn.commit()

    current_page = 1
    while true:
        url = f"{base_url}{page_suffix}{current_page}"
        soup = fetch_data(url)
        if not soup:
            break

        # assume the structure of the site allows us to find titles easily
        titles = soup.find_all('h2', class_='article-title')
        for title in titles:
            save_article(conn, title.text.strip(), none, none)

        next_page_link = soup.find('a', text='next')
        if not next_page_link:
            break
        current_page += 1

    conn.close()

def save_article(conn, title, author, published_date):
    cursor = conn.cursor()
    cursor.execute('''
        insert into articles (title, author, published_date) values (?, ?, ?)
    ''', (title, author, published_date))
    conn.commit()

# example usage
base_url = 'https://www.example.com/articles/'
fetch_pages(base_url)

这个示例将抓取 https://www.example.com/articles/ 上的分页数据,保存文章标题到 sqlite 数据库。注意,你需要根据实际网站的 html 结构调整 find_allfind 方法的参数。

既然我们已经有了一个基本的框架来抓取分页数据并存储到 sqlite 数据库中,现在让我们进一步完善这个代码,包括添加更详细的错误处理、日志记录、以及处理动态加载的网页内容(通常由 javascript 渲染)。

1. 更详细的错误处理

fetch_data 函数中,除了处理请求错误之外,我们还可以捕获和记录其他可能发生的错误,比如解析 html 的错误:

def fetch_data(url):
    try:
        response = requests.get(url)
        response.raise_for_status()
        soup = beautifulsoup(response.text, 'html.parser')
        return soup
    except requests.exceptions.requestexception as e:
        logging.error(f"request error fetching {url}: {e}")
    except exception as e:
        logging.error(f"an unexpected error occurred: {e}")
    return none

2. 更详细的日志记录

在日志记录方面,我们可以增加更多的信息,比如请求的 http 状态码、响应时间等:

import time

def fetch_data(url):
    try:
        start_time = time.time()
        response = requests.get(url)
        elapsed_time = time.time() - start_time
        response.raise_for_status()
        soup = beautifulsoup(response.text, 'html.parser')
        logging.info(f"fetched {url} successfully in {elapsed_time:.2f} seconds, status code: {response.status_code}")
        return soup
    except requests.exceptions.requestexception as e:
        logging.error(f"request error fetching {url}: {e}")
    except exception as e:
        logging.error(f"an unexpected error occurred: {e}")
    return none

3. 处理动态加载的内容

当网站使用 javascript 动态加载内容时,普通的 http 请求无法获取完整的内容。这时可以使用 seleniumpyppeteer 等库来模拟浏览器行为。这里以 selenium 为例:

from selenium import webdriver
from selenium.webdriver.chrome.options import options

def fetch_data_with_js(url):
    options = options()
    options.headless = true  # run chrome in headless mode
    driver = webdriver.chrome(options=options)
    driver.get(url)
    
    # add wait time or wait for certain elements to load
    time.sleep(3)  # wait for dynamic content to load
    
    html = driver.page_source
    driver.quit()
    
    return beautifulsoup(html, 'html.parser')

要使用这段代码,你需要先下载 chromedriver 并确保它在系统路径中可执行。此外,你还需要安装 selenium 库:

pip install selenium

4. 整合所有改进点

现在,我们可以将上述所有改进点整合到我们的分页数据抓取脚本中:

import logging
import time
import requests
from bs4 import beautifulsoup
import sqlite3
from selenium import webdriver
from selenium.webdriver.chrome.options import options

logging.basicconfig(filename='crawler.log', level=logging.info)

def fetch_data(url):
    try:
        start_time = time.time()
        response = requests.get(url)
        elapsed_time = time.time() - start_time
        response.raise_for_status()
        soup = beautifulsoup(response.text, 'html.parser')
        logging.info(f"fetched {url} successfully in {elapsed_time:.2f} seconds, status code: {response.status_code}")
        return soup
    except requests.exceptions.requestexception as e:
        logging.error(f"request error fetching {url}: {e}")
    except exception as e:
        logging.error(f"an unexpected error occurred: {e}")
    return none

def fetch_data_with_js(url):
    options = options()
    options.headless = true
    driver = webdriver.chrome(options=options)
    driver.get(url)
    time.sleep(3)
    html = driver.page_source
    driver.quit()
    return beautifulsoup(html, 'html.parser')

def fetch_pages(base_url, page_suffix='page/', use_js=false):
    conn = sqlite3.connect('data.db')
    cursor = conn.cursor()
    cursor.execute('''
        create table if not exists articles (
            id integer primary key autoincrement,
            title text not null,
            author text,
            published_date date
        )
    ''')
    conn.commit()

    current_page = 1
    fetch_function = fetch_data_with_js if use_js else fetch_data

    while true:
        url = f"{base_url}{page_suffix}{current_page}"
        soup = fetch_function(url)
        if not soup:
            break

        titles = soup.find_all('h2', class_='article-title')
        for title in titles:
            save_article(conn, title.text.strip(), none, none)

        next_page_link = soup.find('a', text='next')
        if not next_page_link:
            break
        current_page += 1

    conn.close()

def save_article(conn, title, author, published_date):
    cursor = conn.cursor()
    cursor.execute('''
        insert into articles (title, author, published_date) values (?, ?, ?)
    ''', (title, author, published_date))
    conn.commit()

# example usage
base_url = 'https://www.example.com/articles/'
use_js = true  # set to true if the site uses js for loading content
fetch_pages(base_url, use_js=use_js)

这个改进版的脚本包含了错误处理、详细的日志记录、以及处理动态加载内容的能力,使得它更加健壮和实用。

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com