问1

我是一个python爬虫的初学者,目前只是学习了HTML的基本语法以及浏览器与服务器间交互的基本流程,我想进一步学习,你有什么好的建议吗?可以进行简单的网页爬取吗?静态网页的爬取?

答1

Python静态网页爬取学习指南

恭喜你已经掌握了HTML基础和浏览器与服务器交互的基本流程,这是学习爬虫非常好的起点!作为初学者,我给你整理了一份清晰的学习路径,特别针对静态网页爬取

一、必备工具安装

首先,你需要安装以下Python库:

pip install requests beautifulsoup4 lxml
  • requests:用于发送HTTP请求获取网页内容
  • beautifulsoup4:用于解析HTML
  • lxml:作为BeautifulSoup的解析器,效率更高

二、静态网页爬取的基本流程

  1. 发送HTTP请求:使用requests.get()获取网页内容
  2. 解析HTML:使用BeautifulSoup解析获取的HTML
  3. 提取数据:通过CSS选择器或XPath定位需要的数据
  4. 存储数据:将数据保存到文件或数据库

三、简单实战示例:爬取名言网站

下面是一个简单的静态网页爬取示例,爬取Quotes to Scrape网站的名言:

import requests
from bs4 import BeautifulSoup

# 设置请求头,模拟浏览器(避免被网站识别为爬虫)
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'
}

# 目标URL
url = 'http://quotes.toscrape.com'

# 发送请求
response = requests.get(url, headers=headers)
response.encoding = 'utf-8'  # 设置编码,避免中文乱码

# 检查请求是否成功
if response.status_code == 200:
    # 使用BeautifulSoup解析HTML
    soup = BeautifulSoup(response.text, 'lxml')
    
    # 提取所有名言(使用CSS选择器)
    quotes = soup.select('div.quote')
    
    # 打印每条名言
    for quote in quotes:
        text = quote.select_one('span.text').text
        author = quote.select_one('small.author').text
        tags = [tag.text for tag in quote.select('div.tags a.tag')]
        
        print(f"名言: {text}")
        print(f"作者: {author}")
        print(f"标签: {', '.join(tags)}")
        print("-" * 50)
else:
    print(f"请求失败,状态码: {response.status_code}")

四、学习建议

  1. 从简单网站开始

    • 选择没有反爬机制的网站练习,如Quotes to Scrape
    • 避免直接爬取大网站,如豆瓣、淘宝等,容易被封IP
  2. 学习HTML和CSS选择器

    • 熟练使用浏览器的开发者工具(F12)查看网页结构
    • 掌握基本的CSS选择器,如div.quotespan.text
  3. 处理常见问题

    • 编码问题:设置response.encoding = 'utf-8'
    • 请求头设置:模拟浏览器User-Agent
    • 异常处理:使用try-except捕获请求异常
  4. 循序渐进

    • 先掌握基本的静态网页爬取
    • 然后学习多页爬取、数据存储(CSV/JSON)
    • 最后再学习动态网页爬取(需要Selenium)

五、重要注意事项

  1. 遵守网站规则

    • 查看网站的robots.txt文件(如http://quotes.toscrape.com/robots.txt)
    • 不要过度请求,设置请求间隔(time.sleep(1)
    • 不要爬取涉及版权、个人隐私的信息
  2. 合法使用

    • 爬虫仅用于学习和研究
    • 不要用于商业用途或侵犯他人权益

六、下一步学习路径

  1. 数据存储:将爬取的数据保存为CSV文件
  2. 多页爬取:爬取网站的多页内容
  3. 数据清洗:处理爬取到的原始数据
  4. 反爬机制应对:了解并应对简单的反爬措施

七、推荐练习网站

  1. Quotes to Scrape - 专为爬虫初学者设计
  2. Douban Movie Top250 - 经典静态网页
  3. The Weather Channel - 适合练习数据提取

解读代码

代码工作流程

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'
}

这段代码定义了一个HTTP请求头(headers)字典,其中包含了一个User-Agent字段。每一部分的含义如下:

  1. headers:这是一个字典变量名,用于存储HTTP请求头信息。

  2. 'User-Agent':这是HTTP请求头中的一个字段,用来标识客户端(浏览器)的类型和版本信息。

  3. 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36':这是User-Agent的具体值,包含了以下信息:

    • Mozilla/5.0:表示使用Mozilla兼容的浏览器
    • Windows NT 10.0; Win64; x64:表示运行在64位Windows 10系统上
    • AppleWebKit/537.36:表示使用WebKit渲染引擎的版本
    • KHTML, like Gecko:表示兼容KHTML引擎(KHTML是类似Gecko的布局引擎)
    • Chrome/91.0.4472.124:表示Chrome浏览器的版本号
    • Safari/537.36:表示也兼容Safari浏览器

为什么要设置这个请求头呢?

  1. 有些网站会检查User-Agent来识别访问者是否是真实浏览器
  2. 可以模拟真实浏览器访问,避免被网站反爬虫机制识别为爬虫程序
  3. 确保服务器返回适合浏览器的内容格式

这个User-Agent字符串模拟的是Chrome浏览器在Windows 10系统上的访问情况,是爬虫程序中最常用的请求头之一。

quotes = soup.select(‘div.quote’)

  1. soup.select('div.quote') 是一个CSS选择器,它会:

    • 查找所有 <div> 标签
    • 这些div标签必须有 class="quote" 属性
    • 返回一个包含所有匹配元素的列表
  2. 在 quotes.toscrape.com 网站中,每条名言都被包装在这样的结构中:

<div class="quote">
    <span class="text">名言内容</span>
    <span class="author">作者名</span>
    <div class="tags">
        <a class="tag" href="/tag/标签名/">标签名</a>
    </div>
</div>
  1. 这行代码获取到的是完整的名言区块,包含了:

    • 名言文本
    • 作者信息
    • 相关标签
    • 以及其他可能包含在这个div中的所有内容
  2. 后续代码通过进一步的选择器(如 quote.select_one('span.text'))来分别提取这些具体信息。

select_one(‘span.ab’)

for quote in quotes:
        text = quote.select_one('span.text').text
        author = quote.select_one('small.author').text
        tags = [tag.text for tag in quote.select('div.tags a.tag')]
        
        print(f"名言: {text}")
        print(f"作者: {author}")
        print(f"标签: {', '.join(tags)}")
        print("-" * 50)

刚刚quotes中包含了所有的名言信息,现在使用for提取出每一个名言的信息,随后使用 select_one(‘标签信息’)得到具体信息,比如

text = quote.select_one('span.text').text

其中的span.text对应的是<span class="text">名言内容</span>。之所以加上.text是为了提取文本。

print(quote.select_one('span.text'))
print(quote.select_one('span.text').text)

得到的输出是

<span class="text" itemprop="text">“A day without sunshine is like, you know, night.”</span>
“A day without sunshine is like, you know, night.”

现在又有一个问题,为什么有的使用select_one有的却使用select呢?

select_oneselect的区别在于返回结果的类型和数量:

  1. select_one():

    • 只返回第一个匹配的元素
    • 返回单个元素对象
    • 如果没找到匹配项,返回None
    • 适合用于确定只有一个的元素(如每条名言只有一个作者)
  2. select():

    • 返回所有匹配的元素列表
    • 返回一个列表,包含所有匹配的元素
    • 如果没找到匹配项,返回空列表[]
    • 适合用于可能有多个的元素(如标签)

在我们的代码中:

text = quote.select_one('span.text').text        # 一条名言只有一个文本内容
author = quote.select_one('small.author').text    # 一条名言只有一个作者
tags = [tag.text for tag in quote.select('div.tags a.tag')]  # 一条名言可能有多个标签

使用场景示例:

  1. 使用select_one的情况:

    • 文章标题(通常只有一个)
    • 文章作者(通常只有一个)
    • 发布日期(通常只有一个)
  2. 使用select的情况:

    • 文章的所有评论(可能有多个)
    • 文章的所有标签(可能有多个)
    • 导航菜单的所有链接(可能有多个)

选择使用哪个方法完全取决于你要获取的元素在页面中是唯一的还是可能有多个。这样可以确保代码更准确、更高效。

爬取CSND文章内容

import requests
from bs4 import BeautifulSoup
import re
import time
import os

def scrape_csdn_article(url):
    """
    爬取CSDN博客文章内容
    
    参数:
        url: CSDN博客文章的URL
    返回:
        包含文章标题、作者、内容的字典
    """
    # 设置请求头,模拟浏览器访问
    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',
        'Accept-Language': 'zh-CN,zh;q=0.9'
    }
    
    try:
        # 发送HTTP请求
        response = requests.get(url, headers=headers)
        response.encoding = 'utf-8'  # 设置编码,避免中文乱码
        
        if response.status_code == 200:
            # 使用BeautifulSoup解析HTML
            soup = BeautifulSoup(response.text, 'lxml')
            
            # 提取文章标题
            title = soup.select_one('h1.title-article')
            article_title = title.text.strip() if title else "无标题"
            
            # 提取作者信息
            author = soup.select_one('span.name')
            article_author = author.text.strip() if author else "未知作者"
            
            # 提取文章内容
            # CSDN文章内容通常在class为"article-content"的div中
            content_div = soup.select_one('div.article-content')
            
            # 如果没有找到标准class,尝试其他可能的class
            if not content_div:
                content_div = soup.select_one('div#content_views')
            
            # 提取文本内容
            article_content = ""
            if content_div:
                # 移除所有HTML标签,只保留纯文本
                # 也可以保留部分HTML,如p、h1-h6、strong等
                for element in content_div.find_all(['p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'strong', 'em', 'ul', 'ol', 'li']):
                    # 移除内部的HTML标签,只保留文本
                    text = element.get_text(strip=True)
                    if text:  # 如果有文本内容
                        article_content += text + "\n\n"
            
            # 清理内容(去除多余空行)
            article_content = re.sub(r'\n{3,}', '\n\n', article_content)
            
            # 返回结果
            return {
                "title": article_title,
                "author": article_author,
                "content": article_content.strip()
            }
        else:
            print(f"请求失败,状态码: {response.status_code}")
            return None
            
    except Exception as e:
        print(f"爬取过程中发生错误: {e}")
        return None

if __name__ == "__main__":
    # 目标URL
    url = "https://blog.csdn.net/qq_73621559/article/details/154644266"
    
    # 爬取文章
    article = scrape_csdn_article(url)
    
    if article:
        # 打印结果
        print(f"标题: {article['title']}")
        print(f"作者: {article['author']}")
        print("\n内容:\n")
        print(article['content'])
        
        # 保存到文件(可选)
        filename = re.sub(r'[\\/*?:"<>|]', "", article['title'])[:50] + ".txt"
        with open(filename, 'w', encoding='utf-8') as f:
            f.write(f"标题: {article['title']}\n")
            f.write(f"作者: {article['author']}\n\n")
            f.write(article['content'])
        print(f"\n文章已保存到文件: {filename}")
    else:
        print("未能获取文章内容")

有兴趣的同学可以尝试一下,我也是叫AI写的,这应该是最简单的了,因为我就问了AI一句就成功了。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐