赞
踩
使用 requests
库的 get
方法请求网站内容,将其解码为文本形式,输出结果验证,完整的代码如下:
import requests # 请求头,添加你的浏览器信息后才可以正常运行 headers= { 'User-Agent': '...', 'Cookie': '...', 'Host': 'www.365kk.cc', 'Connection': 'keep-alive' } # 小说主页 main_url = "http://www.365kk.cc/255/255036/" # 使用get方法请求网页 main_resp = requests.get(main_url, headers=headers) # 将网页内容按utf-8规范解码为文本形式 main_text = main_resp.content.decode('utf-8') print(main_text)
运行代码前,需要向
headers
中填入你自己浏览器的信息。
输出结果如下:
可以看出,我们成功请求到了网站内容,接下来只需对其进行解析,即可得到我们想要的部分。
我们使用 lxml
库来解析网页内容,具体方法为将文本形式的网页内容创建为可解析的元素,再按照XPath路径访问其中的内容,代码如下:
import requests from lxml import etree # 请求头 headers= { 'User-Agent': '...', 'Cookie': '...', 'Host': 'www.365kk.cc', 'Connection': 'keep-alive' } # 小说主页 main_url = "http://www.365kk.cc/255/255036/" # 使用get方法请求网页 main_resp = requests.get(main_url, headers=headers) # 将网页内容按utf-8规范解码为文本形式 main_text = main_resp.content.decode('utf-8') # 将文本内容创建为可解析元素 main_html = etree.HTML(main_text) # 依次获取书籍的标题、作者、最近更新时间和简介 # main\_html.xpath返回的是列表,因此需要加一个[0]来表示列表中的首个元素 bookTitle = main_html.xpath('/html/body/div[4]/div[1]/div/div/div[2]/div[1]/h1/text()')[0] author = main_html.xpath('/html/body/div[4]/div[1]/div/div/div[2]/div[1]/div/p[1]/text()')[0] update = main_html.xpath('/html/body/div[4]/div[1]/div/div/div[2]/div[1]/div/p[5]/text()')[0] introduction = main_html.xpath('/html/body/div[4]/div[1]/div/div/div[2]/div[2]/text()')[0] # 输出结果以验证 print(bookTitle) print(author) print(update) print(introduction)
输出结果如下:
至此,我们已经学会了基本的网页请求方法,并学会了如何获取目标页面中的特定内容。
接下来,我们开始爬取正文。首先尝试获取单个页面的数据,再尝试设计一个循环,依次获取所有正文数据。
以第一章为例,链接:http://www.365kk.cc/255/255036/4147599.html,获取章节标题和正文的XPath路径如下:
//*[@id="container"]/div/div/div[2]/h1/text()
//*[@id="content"]/text()
按照与上文一致的方法请求并解析网页内容,代码如下:
import requests from lxml import etree # 请求头 headers= { 'User-Agent': '...', 'Cookie': '...', 'Host': 'www.365kk.cc', 'Connection': 'keep-alive' } # ... # 上一部分的代码 # ... # 当前页面链接 url = 'http://www.365kk.cc/255/255036/4147599.html' resp = requests.get(url, headers) text = resp.content.decode('utf-8') html = etree.HTML(text) title = html.xpath('//\*[@id="container"]/div/div/div[2]/h1/text()')[0] contents = html.xpath('//\*[@id="content"]/text()') print(title) for content in contents: print(content)
输出结果如下:
可以看出,我们成功获取了小说第一章第一页的标题和正文部分,接下来我们将它储存在一个txt文本文档中,文档命名为之前获取的书名 bookTitle.txt
,完整的代码如下:
import requests from lxml import etree # 请求头 headers= { 'User-Agent': '...', 'Cookie': '...', 'Host': 'www.365kk.cc', 'Connection': 'keep-alive' } # 小说主页 main_url = "http://www.365kk.cc/255/255036/" # 使用get方法请求网页 main_resp = requests.get(main_url, headers=headers) # 将网页内容按utf-8规范解码为文本形式 main_text = main_resp.content.decode('utf-8') # 将文本内容创建为可解析元素 main_html = etree.HTML(main_text) bookTitle = main_html.xpath('/html/body/div[4]/div[1]/div/div/div[2]/div[1]/h1/text()')[0] author = main_html.xpath('/html/body/div[4]/div[1]/div/div/div[2]/div[1]/div/p[1]/text()')[0] update = main_html.xpath('/html/body/div[4]/div[1]/div/div/div[2]/div[1]/div/p[5]/text()')[0] introduction = main_html.xpath('/html/body/div[4]/div[1]/div/div/div[2]/div[2]/text()')[0] # 当前页面链接 url = 'http://www.365kk.cc/255/255036/4147599.html' resp = requests.get(url, headers) text = resp.content.decode('utf-8') html = etree.HTML(text) title = html.xpath('//\*[@id="container"]/div/div/div[2]/h1/text()')[0] contents = html.xpath('//\*[@id="content"]/text()') with open(bookTitle + '.txt', 'w', encoding='utf-8') as f: f.write(title) for content in contents: f.write(content) f.close()
运行结束后,可以看到在代码文件的同路径中,已经生成了一个文本文档:
打开该文档,可以看到储存好的内容:
储存好的内容中,大段的文字堆积在一起,而原文确实有着段落的区分,因此我们在储存文件时,每储存一段,就写入两个换行符 \n
,使格式更便于阅读:
with open(bookTitle + '.txt', 'w', encoding='utf-8') as f:
f.write(title)
for content in contents:
f.write(content)
f.write('\n\n')
f.close()
至此,我们已经完成了单个页面的数据爬取和存储,接下来只要设计循环,实现顺序爬取所有页面即可。
本文中,用到的文件读取类型主要有:
类型 | 含义 | 使用示例 |
---|---|---|
'w' | 清空原文档,重新写入文档 | open(filename, 'w') |
'r' | 仅读取文档,不改变其内容 | open(filename, 'r') |
'a' | 在原文档之后追加内容 | open(filename, 'a') |
我们注意到,正文的每个页面底部,都有一个按钮下一页,其在网页中的结构为:
我们在XPath路径的末尾添加 @href
用于获取属性 href
的值:
//*[@id="container"]/div/div/div[2]/div[3]/a[3]/@href
这里有一个小细节需要注意!
我们获取到的属性值在不同页面可能是不一样的,比如:
/255/255036/4147599_2.html
4147600.html
观察不同页面的链接,可以看出前缀是一致的,区别仅在后缀上,比如第一章第一页和第一章第二页的链接分别为:
http://www.365kk.cc/255/255036/4147599.html
http://www.365kk.cc/255/255036/4147599_2.html
因此,我们只需要获取 {% kbd 下一页 %} 的链接后缀,再与前缀拼接,即可获得完整的访问链接。
编写一个函数 next_url()
实现上述功能:
# 获取下一页链接的函数 def next\_url(next_url_element): nxturl = 'http://www.365kk.cc/255/255036/' # rfind('/') 获取最后一个'/'字符的索引 index = next_url_element.rfind('/') + 1 nxturl += next_url_element[index:] return nxturl # 测试一下 url1 = '/255/255036/4147599\_2.html' url2 = '4147600.html' print(next_url(url1)) print(next_url(url2))
输出如下所示:
在爬取某一页面的内容后,我们获取下一页的链接,并请求该链接指向的网页,重复这一过程直到全部爬取完毕为止,即可实现正文的爬取。
在这一过程中,需要注意的问题有:
按照上述思想,爬取前6个页面作为测试,完整的代码如下:
import requests from lxml import etree import time import random # 获取下一页链接的函数 def next\_url(next_url_element): nxturl = 'http://www.365kk.cc/255/255036/' # rfind('/') 获取最后一个'/'字符的索引 index = next_url_element.rfind('/') + 1 nxturl += next_url_element[index:] return nxturl # 请求头,需要添加你的浏览器信息才可以运行 headers= { 'User-Agent': '...', 'Cookie': '...', 'Host': 'www.365kk.cc', 'Connection': 'keep-alive' } # 小说主页 main_url = "http://www.365kk.cc/255/255036/" # 使用get方法请求网页 main_resp = requests.get(main_url, headers=headers) # 将网页内容按utf-8规范解码为文本形式 main_text = main_resp.content.decode('utf-8') # 将文本内容创建为可解析元素 main_html = etree.HTML(main_text) bookTitle = main_html.xpath('/html/body/div[4]/div[1]/div/div/div[2]/div[1]/h1/text()')[0] author = main_html.xpath('/html/body/div[4]/div[1]/div/div/div[2]/div[1]/div/p[1]/text()')[0] update = main_html.xpath('/html/body/div[4]/div[1]/div/div/div[2]/div[1]/div/p[5]/text()')[0] introduction = main_html.xpath('/html/body/div[4]/div[1]/div/div/div[2]/div[2]/text()')[0] # 调试期间仅爬取六个页面 maxPages = 6 cnt = 0 # 记录上一章节的标题 lastTitle = '' # 爬取起点 url = 'http://www.365kk.cc/255/255036/4147599.html' # 爬取终点 endurl = 'http://www.365kk.cc/255/255036/4148385.html' while url != endurl: cnt += 1 # 记录当前爬取的页面 if cnt > maxPages: break # 当爬取的页面数超过maxPages时停止 resp = requests.get(url, headers) text = resp.content.decode('utf-8') html = etree.HTML(text) title = html.xpath('//\*[@class="title"]/text()')[0] contents = html.xpath('//\*[@id="content"]/text()') # 输出爬取进度信息 print("cnt: {}, title = {}, url = {}".format(cnt, title, url)) with open(bookTitle + '.txt', 'a', encoding='utf-8') as f: if title != lastTitle: # 章节标题改变 f.write(title) # 写入新的章节标题 lastTitle = title # 更新章节标题 for content in contents: f.write(content) f.write('\n\n') f.close() # 获取"下一页"按钮指向的链接 next_url_element = html.xpath('//\*[@class="section-opt m-bottom-opt"]/a[3]/@href')[0] # 传入函数next\_url得到下一页链接 url = next_url(next_url_element) sleepTime = random.randint(2, 5) # 产生一个2~5之间的随机数 time.sleep(sleepTime) # 暂停2~5之间随机的秒数 print("complete!")
运行结果如下:
观察我们得到的文本文档,可以发现如下问题:
为了解决这些问题,我们编写一个函数 clean_data()
来实现数据清洗,代码如下:
def clean\_data(filename, info): """ :param filename: 原文档名 :param info: [bookTitle, author, update, introduction] """ print("\n==== 数据清洗开始 ====") # 新的文件名 new_filename = 'new' + filename # 打开两个文本文档 f_old = open(filename, 'r', encoding='utf-8') f_new = open(new_filename, 'w', encoding='utf-8') # 首先在新的文档中写入书籍信息 f_new.write('== 《' + info[0] + '》\r\n') # 标题 f_new.write('== ' + info[1] + '\r\n') # 作者 f_new.write('== ' + info[2] + '\r\n') # 最后更新时间 f_new.write("=" \* 10) f_new.write('\r\n') f_new.write('== ' + info[3] + '\r\n') # 简介 f_new.write("=" \* 10) f_new.write('\r\n') lines = f_old.readlines() # 按行读取原文档中的内容 empty_cnt = 0 # 用于记录连续的空行数 # 遍历原文档中的每行 for line in lines: if line == '\n': # 如果当前是空行 empty_cnt += 1 # 连续空行数+1 if empty_cnt >= 2: # 如果连续空行数不少于2 continue # 直接读取下一行,当前空行不写入 else: # 如果当前不是空行 empty_cnt = 0 # 连续空行数清零 if line.startswith("\u3000\u3000"): # 如果有段首缩进 line = line[2:] # 删除段首缩进 f_new.write(line) # 写入当前行 elif line.startswith("第"): # 如果当前行是章节标题 f_new.write("\r\n") # 写入换行 f_new.write("-" \* 20) # 写入20个'-' f_new.write("\r\n") # 写入换行 f_new.write(line) # 写入章节标题 else: # 如果当前行是未缩进的普通段落 f_new.write(line) # 保持原样写入 f_old.close() # 关闭原文档 f_new.close() # 关闭新文档
清洗后的文档中,每段段首无缩进,段与段之间仅空一行,不同章节之间插入20个字符 -
用以区分,上述问题得以解决。
需先填入自己浏览器的
headers
信息才可以直接运行!
import requests from lxml import etree import time import random # 获取下一页链接的函数 def next\_url(next_url_element): nxturl = 'http://www.365kk.cc/255/255036/' # rfind('/') 获取最后一个'/'字符的索引 index = next_url_element.rfind('/') + 1 nxturl += next_url_element[index:] return nxturl # 数据清洗函数 def clean\_data(filename, info): """ :param filename: 原文档名 :param info: [bookTitle, author, update, introduction] """ print("\n==== 数据清洗开始 ====") # 新的文件名 new_filename = 'new' + filename **自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。** **深知大多数Python工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!** **因此收集整理了一份《2024年Python开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。**       **既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!** **由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新** **如果你觉得这些内容对你有帮助,可以扫码获取!!!(备注:Python)** ** **因此收集整理了一份《2024年Python开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。** [外链图片转存中...(img-XLIgc0Fg-1713678582494)] [外链图片转存中...(img-ymwGHND5-1713678582495)] [外链图片转存中...(img-8SB2gKQw-1713678582496)] [外链图片转存中...(img-ypah9Nxj-1713678582496)]   **既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!** **由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新** **如果你觉得这些内容对你有帮助,可以扫码获取!!!(备注:Python)** 
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。