前言
python实现网络爬虫非常简单,只需要掌握一定的基础知识和一定的库使用技巧即可。本系列目标旨在梳理相关知识点,方便以后复习。
申明
本系列所涉及的代码仅用于个人研究与讨论,并不会对网站产生不好影响。
目录结构
这次爬虫实战,采用的库为:requests + lxml
,这次以爬取一部小说为目标,具体的网站老规矩就不给了,大家学习思路最重要。
再次说明,案例本身并不重要,重要的是如何去使用和分析,另外为了避免侵权之类的问题,我不会放涉及到网站的图片,希望能理解。
第一步,确定get请求的url
假设我们的网站为:https://xxxxxxx.com
,那么,我们首先需要找到搜索框,然后随意搜索几本小说,比如这里我搜索的是圣墟
、万族之劫
,那么观察网页上的url变化,如下:
https://xxxxx?q=圣墟
https://xxxxx?q=万族之劫
可以看出,这里是get
请求,并且参数名为q
。
第二步,正确请求网页
我们可以写下第一个代码了,目标是获取想要的小说,代码如下:
# 都要用到的参数
HEADERS = {
'User-Agent' : 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36',
}
# 获取搜索某小说后的页面
def get_search_result():
# 网址
url = 'xxxxx'
# 请求参数
search = input('请输入想要搜索的小说:')
params = {
'q' : search
}
# 请求
response = requests.get(url,headers=HEADERS,params=params)
# 把获取到的网页保存到本地
with open('search.html','w',encoding='utf-8') as f:
f.write(response.content.decode('utf-8'))
结果如下:
可以看出,获取到正确的网页,说明这一步我们完成了。
正确解析网页,以获取小说链接
上面,我们已经把网页源码存放到了本地一个名为search.html
的文件,下面我们来解析它。
解析它,首先需要明确我们要获取什么节点、什么值。看下面:
可以看出,我们的目标标签位于div[class="mshow"]下的table[class="grid"]下的td下的a标签
,并且我们需要获取这个a标签的href属性和文本值。除此之外,href属性值只是部分地址,需要跟网站根地址xxxx.com
拼凑再一起。
基于此,可以完成代码:
# 解析网页
def parse_search_result():
# 打开文件,读取文件
with open('search.html','r',encoding='utf-8') as f:
content = f.read()
# 基础url
base_url = 'xxxxxx.com/'
# 初始化lxml
html = etree.HTML(content)
# 获取目标节点
href_list = html.xpath('//div[@class="show"]//table[@class="grid"]//td//a/@href')
text_list = html.xpath('//div[@class="show"]//table[@class="grid"]//td//a/text()')
# 处理内容值
url_list = [base_url+href for href in href_list]
# 选择要爬取的小说
for i,text in enumerate(text_list):
print('当前小说名为:',text)
decision = input('是否爬取它(只能选择一本),Y/N:')
if decision == 'Y':
return url_list[i],text
运行结果如下:
第一步,请求页面
首先,我们去请求上面获取的网页,这里就比较简单了,基本上把上面的请求代码拷贝过来修改修改即可:
# 请求目标小说网站
def get_target_book(url):
# 请求
response = requests.get(url,headers=HEADERS)
# 保存源码
with open('book.html','w',encoding='utf-8') as f:
f.write(response.content.decode('utf-8'))
可以看出,这一步成功了。
第二步,解析上面的网页,获取不同章节的链接
这一步,主要的难点在于解析网页,首先,看下面:
由于该网页小说章节都分为两个部位,第一个为最新章节
,第二个为全部章节
,而第二个才是我们需要获取的,因此xpath语法应该为:
//div[@class="show"]//div[contains(@class,'showBox') and position()=3]//ul//a
那么,可以完成代码如下:
# 解析章节网页
def parse_chapter(base_url):
# 打开文件,读取内容
with open('book.html','r',encoding='utf-8') as f:
content = f.read()
# 初始化
html = etree.HTML(content)
# 解析
href_list = html.xpath('//div[@class="show"]//div[contains(@class,"showBox") and position()=3]//ul//a/@href')
text_list = html.xpath('//div[@class="show"]//div[contains(@class,"showBox") and position()=3]//ul//a/text()')
# 处理:拼凑出完整网页
url_list = [base_url+url for url in href_list]
# 返回结果
return url_list,text_list
运行结果如下:
这里我们就不分开了,直接获取源码后直接解析。那么这里说明一下解析原理,看下面:
可以轻松知道xpath语法:
//div[contains(@class,'book')]//div[@id='content']//text()
那么,代码如下:
# 请求小说页面
def get_content(url,title):
# 请求
response = requests.get(url,headers=HEADERS)
# 获取源码
content = response.content.decode('utf-8')
# 初始化
html = etree.HTML(content)
# 解析
text_list = html.xpath('//div[contains(@class,"book")]//div[@id="content"]//text()')
# 后处理
# 首先,把第一个和最后一个的广告信息去掉
text_list = text_list[1:-1]
# 其次,把里面的空白字符和\xa0去掉
text_list = [text.strip().replace('\xa0','') for text in text_list]
# 最后,写入文件即可
with open(title+'.txt','w',encoding='utf-8') as g:
for text in text_list:
g.write(text+'\n')
运行结果如下:
可以看出,成功实现。
完整代码如下:
# author : 自学小白菜
# -*- coding:utf-8 -*-
'''
# File Name : 7 lxml_novel.py
# Create Time : 2023/8/5 22:04
# Version : python3.7
# Description : 实战1:爬取小说
'''
# 导包
import requests
from lxml import etree
# 都要用到的参数
HEADERS = {
'User-Agent' : 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36',
}
# 获取搜索某小说后的页面
def get_search_result():
# 网址
url = 'https://www.iwurexs.net/so.html'
# 请求参数
search = input('请输入想要搜索的小说:')
params = {
'q' : search
}
# 请求
response = requests.get(url,headers=HEADERS,params=params)
# 把获取到的网页保存到本地
with open('search.html','w',encoding='utf-8') as f:
f.write(response.content.decode('utf-8'))
# 解析网页
def parse_search_result():
# 打开文件,读取文件
with open('search.html','r',encoding='utf-8') as f:
content = f.read()
# 基础url
base_url = 'https://www.iwurexs.net/'
# 初始化lxml
html = etree.HTML(content)
# 获取目标节点
href_list = html.xpath('//div[@class="show"]//table[@class="grid"]//td//a/@href')
text_list = html.xpath('//div[@class="show"]//table[@class="grid"]//td//a/text()')
# 处理内容值
url_list = [base_url+href for href in href_list]
# 选择要爬取的小说
for i,text in enumerate(text_list):
print('当前小说名为:',text)
decision = input('是否爬取它(只能选择一本),Y/N:')
if decision == 'Y':
return url_list[i],text
# 请求目标小说网站
def get_target_book(url):
# 请求
response = requests.get(url,headers=HEADERS)
# 保存源码
with open('book.html','w',encoding='utf-8') as f:
f.write(response.content.decode('utf-8'))
# 解析章节网页
def parse_chapter(base_url):
# 打开文件,读取内容
with open('book.html','r',encoding='utf-8') as f:
content = f.read()
# 初始化
html = etree.HTML(content)
# 解析
href_list = html.xpath('//div[@class="show"]//div[contains(@class,"showBox") and position()=3]//ul//a/@href')
text_list = html.xpath('//div[@class="show"]//div[contains(@class,"showBox") and position()=3]//ul//a/text()')
# 处理:拼凑出完整网页
url_list = [base_url+url for url in href_list]
# 返回结果
return url_list,text_list
# 请求小说页面
def get_content(url,title):
# 请求
response = requests.get(url,headers=HEADERS)
# 获取源码
content = response.content.decode('utf-8')
# 初始化
html = etree.HTML(content)
# 解析
text_list = html.xpath('//div[contains(@class,"book")]//div[@id="content"]//text()')
# 后处理
# 首先,把第一个和最后一个的广告信息去掉
text_list = text_list[1:-1]
# 其次,把里面的空白字符和\xa0去掉
text_list = [text.strip().replace('\xa0','') for text in text_list]
# 最后,写入文件即可
with open(title+'.txt','w',encoding='utf-8') as g:
for text in text_list:
g.write(text+'\n')
if __name__ == '__main__':
# 第一步,获取到搜索页面的源码
# get_search_result()
# 第二步,进行解析
target_url,name = parse_search_result()
# 第三步,请求目标小说页面
get_target_book(target_url)
# 第四步,解析章节网页
url_list,text_list = parse_chapter(target_url)
for url,title in zip(url_list,text_list):
# 第五步,请求小说具体的某个章节并直接解析
get_content(url,title)
break
上面代码还不完善,存在一定优化的地方,比如代码有些地方可以解耦,另外,必须限制访问速度,不然后期容易被封掉IP,除此之外,可以考虑代理池构建等操作。