网络爬虫实战:从零开始爬取顶点小说全攻略
在当今数据为王的时代,网络爬虫技术已成为获取海量网络信息的重要工具。无论是数据分析师、程序开发者,还是对网络数据感兴趣的普通用户,掌握基础的爬虫技能都能让您在信息获取方面事半功倍。本文将通过顶点小说网站的实例,带您系统了解网络爬虫的开发流程与核心技术。
一、爬虫基础知识
1、爬虫的分类与原理
网络爬虫按照爬取范围可分为两大类:
- 搜索引擎爬虫:爬取范围广泛,如百度、Google等搜索引擎使用的爬虫
- 聚焦爬虫:针对特定网站或特定内容进行定向爬取
爬虫的基本原理非常简单:程序发起请求(request),获取响应(response),然后解析 response 中的数据。这个过程模拟了人类使用浏览器访问网页的行为。
2、URL 的组成部分
URL(统一资源定位符)是互联网上标准资源的地址,通常由以下几部分组成:
-
协议:
- http:超文本传输协议
- https:在 http 基础上新增 SSL 协议(证书验证),提高安全性。现在大部分网站都采用 https 协议,虽然有轻微的性能损耗,但可以忽略不计
-
主机 IP 地址:有时也包括端口号
-
主机资源具体地址:如目录和文件名等
3、静态网站与动态网站
理解网站类型对爬虫开发至关重要:
- 静态网站:数据直接存在于页面源代码中,爬取相对简单
- 动态网站:数据存储在接口中,网站通过 ajax 请求获取数据,再通过 JavaScript 渲染到页面,爬取难度较大
4、结构化与非结构化数据
爬虫获取的数据可分为:
- 结构化数据:可以用关系型数据库表示和存储,表现为二维形式的数据
- 非结构化数据:数据结构不规则,不方便用二维逻辑表现,如办公文档、图片、HTML、音频和视频等
二、爬虫开发工具准备
1、XPath Helper 插件安装
XPath Helper Plus 是一个非常实用的浏览器插件,可以帮助开发者快速定位网页元素。
安装提示:
如果加载完插件后点击没有反应,请将浏览器的开发人员模式关闭即可。
2、XPath 语法介绍
XPath(XML Path Language)是用于确定 XML 文档中某部分位置的语言。Python 可以使用 XPath 语法定位 HTML 文档中的元素并进行数据抽取。
常用语法:
/
:从文档根目录开始选取//
:全局进行查找选取,相当于正则表达式的.*?
//li/a/text()
:获取所有 li 下 a 标签的文本内容//li//text()
:获取 li 标签下所有文本(不区分标签),仅限深度为 1//a[@class="poptext"]
:选取带有 class 属性值为 “poptext” 的 a 标签@href
:获取元素的 href 属性值*
:匹配任何元素节点,如//*
、/bookstore/*
|
:组合多个 XPath 表达式,如//book/title | //book/price
动态网站解析说明:
动态网站能否使用 XPath 解析取决于数据返回格式。如果接口返回 HTML 文档,则可用 XPath 解析;如果返回 JSON 数据,则不能使用 XPath。本质上,XPath 是否可用取决于数据是否为节点数据(是否具有 DOM 结构)。
三、顶点小说爬取实战
1、环境准备与基础设置
首先导入必要的库,并设置请求头:
import requests
from lxml import etree
import pymysql
# 设置请求头,模拟浏览器访问
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36 Edg/126.0.0.0"
}
2、获取小说分类 URL
# 获取小说分类 URL
def get_type():
url = "https://www.cdbxs.com/sort/"
source = requests.get(url=url, headers=headers).content.decode('utf-8')
href_lists = etree.HTML(source).xpath('//ul[@class="nav"]/li/a/@href')[2:-4]
type_lists = []
for href in href_lists:
type_lists.append(f"{url}{href.split('/')[2]}/1/")
return type_lists
3、获取分类页面的最大页数
# 获取最大页数
def get_max_page(first_page_url):
source = requests.get(url=first_page_url, headers=headers).content.decode('utf-8')
max_page = etree.HTML(source).xpath('//a[13]/text()')
return max_page
4、获取每个分类的分页 URL
# 获取小说分类 URL
type_lists = get_type()
# 分类 URL 默认为第一页
for first_page_url in type_lists:
# 获取分类 URL 的前半部分
type_url = first_page_url.split('1')[0]
# 获取此分类下最大页数
max_page = get_max_page(first_page_url)
# 生成此分类下每一页 URL
for every_page in range(1, int(max_page[0])+1):
every_page_url = f"{type_url}{every_page}/"
print(every_page_url)
5、获取小说列表页信息
def get_book_info(every_page_url):
source = requests.get(url=every_page_url, headers=headers).content.decode('utf-8')
book_lists = []
lis = etree.HTML(source).xpath("//ul[@class='txt-list txt-list-row5']/li")
for li in lis:
book_id_url = li.xpath("span[@class='s2']/a/@href")[0]
book_id = book_id_url.split('/')[3]
# 书名
book_name = li.xpath("span[@class='s2']/a/text()")[0]
# 最新章节
new_chapter = li.xpath("span[@class='s3']/a/text()")[0]
# 作者
author = li.xpath("span[@class='s4']/text()")[0]
# 更新时间
update_time = li.xpath("span[@class='s5']/text()")[0]
source = requests.get(url=f"https://www.cdbxs.com{book_id_url}", headers=headers).content.decode('utf-8')
# 字数
font_num = etree.HTML(source).xpath("//p[6]/span/text()")[0]
# 摘要
summary = etree.HTML(source).xpath("//div[@class='desc xs-hidden']/text()")[0]
# 以元组形式添加至 book_lists
book_lists.append((book_id, book_name, new_chapter, author, update_time, font_num, summary))
return book_lists
6、获取章节列表 URL
# 获取章节列表 URL
def get_chapter_urls(chapter_list_url):
source = requests.get(url=chapter_list_url, headers=headers).content.decode('utf-8')
# 章节 URL
chapter_urls = map(lambda x: "https://www.cdbxs.com" + x, etree.HTML(source).xpath("//div[@class='section-box'][2]/ul[@class='section-list fix']/li/a/@href | //div[@class='section-box'][1]/ul[@class='section-list fix']/li/a/@href"))
return chapter_urls
7、获取章节详情信息
# 获取章节详情信息
def get_chapter_info(chapter_url):
source = requests.get(url=chapter_url, headers=headers).content.decode('utf-8')
# 标题
title = etree.HTML(source).xpath("//h1[@class='title']/text()")
# 正文
content = ''.join(etree.HTML(source).xpath("//div[@id='nb_content']/dd//text()"))
if title:
return title[0], content
else:
return '', content
8、整合爬虫流程
# 获取小说分类 URL
type_lists = get_type()
# 分类 URL 默认为第一页
for first_page_url in type_lists:
# 获取分类 URL 的前半部分
type_url = first_page_url.split('1')[0]
# 获取此分类下最大页数
max_page = get_max_page(first_page_url)
# 生成此分类下每一页 URL
for every_page in range(1, int(max_page[0]) + 1):
every_page_url = f"{type_url}{every_page}/"
# 获取小说列表页信息
book_info_lists = get_book_info(every_page_url)
# 获取章节列表 URL
for book_info in book_info_lists:
print(f"爬取小说:{book_info[1]}...")
book_id = book_info[0]
chapter_urls = get_chapter_urls(f"https://www.cdbxs.com/booklist/b/{book_id}/1")
for chapter_url in chapter_urls:
chapter_info = get_chapter_info(chapter_url)
print(chapter_info[0]) # 打印章节标题
print(chapter_info[1]) # 打印章节内容
四、爬虫技术进阶方向
本教程介绍了基础的爬虫开发流程,但在实际应用中,还可以进一步优化:
- 数据存储:将爬取的数据存入 MySQL、MongoDB 等数据库
- 多线程/多进程:提高爬取效率,大幅减少爬取时间
- 反爬处理:应对网站的反爬机制,如 IP 代理池、Cookie 池、请求延时等
- 分布式爬虫:利用多台服务器协同工作,进一步提升爬取效率
五、小结与展望
本文通过顶点小说网站的实例,详细介绍了网络爬虫的基本原理和开发流程。从 URL 结构、XPath 定位到数据提取,系统地展示了爬虫开发的各个环节。掌握这些基础知识后,您可以根据自己的需求,开发出适用于不同网站的爬虫程序。
关注公众号【码途有你】,后续将为大家带来更多爬虫进阶内容,包括但不限于数据入库、多线程多进程爬取、反爬机制应对等技术内容!