2025年4月12日 星期六 乙巳(蛇)年 正月十三 设为首页 加入收藏
rss
您当前的位置:首页 > 计算机 > 编程开发 > Python

python爬虫,抓取完整小说、图片

时间:06-28来源:作者:点击数:53

虽然以前接触过爬虫,但是也很久没有去做过相关的编码了,正好最近有需求要收集一些图片和txt,就还是一步一个脚印,玩了一下。

本文从简单的框架开始,爬取图片和爬取txt分别进行介绍。如何访问网址,打印html,分析数据,正则匹配,文件I/O,到最后爬取数据,以及如何基础的规避网站安全系统。

新手可跟着本文的节奏同步分析,只对代码感兴趣的,可直接跳转到最后。

环境

python3+pycharm

安装第三方库requests和urllib3,这俩可在pycharm中直接安装,很少被墙

1.爬取图片

图片网址:https://page.om.qq.com/page/OGosuxbOv7M6LKD__rB7FeSA0.

浏览器访问这个地址,然后F12,查看源码,可以发现,每张图片在iframe里有一个地址。通过正则匹配出这些地址,然后下载地址中的内容即可。

1.1第一步、访问连接,获取html

库导入

  • import urllib.request
  • import requests
  • import re
  • import os
  • import time

创建picture文件夹用于存放

  • if not os.path.exists('picture'):
  • os.mkdir('picture')

通过requests里的get方法访问地址,并获取html

  • html = 'https://page.om.qq.com/page/OGosuxbOv7M6LKD__rB7FeSA0'
  • url = requests.get(html)
  • htmlcode=url.content

将html打印到本地

  • pagefile = open('picture/picture.txt','wb')
  • pagefile.write(htmlcode)
  • pagefile.close()

这时就可在工程下的picture文件夹中,更直观的查看html结构

1.2分析html,正则匹配

在这里插入图片描述

规则还是很明显的,由 preview-src=" 开头, "> 结尾,中间则为完整的链接。

  • reg = r'preview-src="(.+)">'
  • reg_img = re.compile(reg)
  • imglist = reg_img.findall(htmlcode.decode())

这里需要注意一下转码,因为用content获取的html为bytes格式,但是findall是针对str的。

此时可打印一下验证是否得到了所有的图片链接

1.3下载链接中的图片

urllib库request类中,urlretrieve方法可下载链接,并保存到具体路径。

对地址列表进行遍历下载,同时为防止因本地网络或者网站原因导致报错,加一个try

  • flag = 1
  • for img in imglist:
  • try:
  • urllib.request.urlretrieve(img,'picture/%d.png'%flag)
  • print('第%d张图片下载完成'%flag)
  • except:
  • print('第%d张图片下载异常,请稍后再试'%flag)
  • flag += 1

1.4爬取图片的完整代码

  • import urllib.request
  • import requests
  • import re
  • import os
  • import time
  • if not os.path.exists('picture'):
  • os.mkdir('picture')
  • #图片来源地址
  • html = 'https://page.om.qq.com/page/OGosuxbOv7M6LKD__rB7FeSA0'
  • #获取网页html
  • url = requests.get(html)
  • htmlcode=url.content
  • #将html打印到txt中
  • pagefile = open('picture/picture.txt','wb')
  • pagefile.write(htmlcode)
  • pagefile.close()
  • reg = r'preview-src="(.+)">'
  • reg_img = re.compile(reg)
  • imglist = reg_img.findall(htmlcode.decode())
  • #print(imglist)
  • time.sleep(1)
  • flag = 1
  • for img in imglist:
  • #print(img)
  • try:
  • urllib.request.urlretrieve(img,'picture/%d.png'%flag)
  • print('第%d张图片下载完成'%flag)
  • except:
  • print('第%d张图片下载异常,请稍后再试'%flag)
  • flag += 1

将html打印到TXT的代码可注释掉,只是我个人习惯在nodepad++中分析html,方便查找正则规律

2.爬取完整小说

爬取TXT的框架类似,只是如果需要爬取一整部小说的内容,需要注意的点还是挺多的,而且需要进行伪装,和网站的安全系统斗智斗勇。

小说链接:https://www.ddxs.cn/book/195261/.

可从具体每一章节中看到,文字都直接存在html中。

首先需要进入小说的章节列表地址,从列表html获取每一章的地址,然后再从具体章节的html中抓取正文,最后优化格式,输出即可

导入库信息

  • import os
  • import requests
  • import re
  • import time
  • import random

2.1自定义获取网页html方法

  • def get_htmlcode(url):
  • try:
  • urlhtml = requests.get(url)
  • htmlcode = urlhtml.content
  • # path = open('Txt/html.txt','wb')
  • # path.write(htmlcode)
  • # path.close()
  • except:
  • htmlcode = 'error'
  • return htmlcode

形参为网络url,会返回网页的html。

打印html的代码块被我注释了,调试过程中,可取消注释,辅助分析

2.2处理章节列表地址html方法

在这里插入图片描述

经过观察可以发现有 最新章节 和章节目录 两块,结构很相似。直接匹配 <a href="/ 开头和 " title= 结尾 ,会将最新章节里的地址也匹配下来,这一块是存在重复的,我们只需要章节目录下的数据,所以需要对html进行处理。以 章节目录 为分割线,之后所有匹配的地址则是我们需要的。

对元素值‘章节目录’进行查找,返回索引值,然后以索引下标进行切片,只保留‘章节目录’后的数据。再对处理后的html进行正则就可以获取每一章的地址。

  • def get_chapter_list(htmlcode):
  • htmldecode = htmlcode.decode()
  • a = htmldecode.index('章节目录')
  • htmldecode_final = htmldecode[a:]
  • reg = r'<a href="(.+?)" title="八荒凤鸣'
  • reg_msg = re.compile(reg)
  • chapter_list = reg_msg.findall(htmldecode_final)
  • return chapter_list

这里得到的还不是完整的地址,缺少了这个网址的前缀’https://www.ddxs.cn’。在后面的代码中注意这个点,加上就好

2.3爬取章节名和正文的方法

2.2中已经获取到了所有章节的地址列表,通过2.1的方法再获取任意一个章节的html,发现每个章节地址的html格式规律都是相同的,只要遍历地址列表即可。

这里又分两步,匹配章节名、正文的正则表达式规则是不同的

在这里插入图片描述

匹配章节名

  • // 获取章节名称
  • def get_subject(htmlcode):
  • reg = '<h1>(.+?)</h1>'
  • reg_msg = re.compile(reg)
  • subject = reg_msg.findall(htmlcode.decode())
  • return subject

匹配正文

  • // 获取正文
  • def get_novel(htmlcode):
  • reg = 'class="content"><p>(.+?)</p></article>'
  • reg_msg = re.compile(reg)
  • novel = reg_msg.findall(htmlcode.decode())
  • # print(novel)
  • return novel

这里可以分别打印爬取的数据,验证下正则表达式的逻辑是否正确

2.4 优化正文格式

在2.3中,爬取的正文是一整行的数据,中间杂糅着很多</p><p>。这些就是段落的规律,我们只需要把</p>替换成换行,<p>替换成缩进,就可以形成很符合咱们观看习惯的排版。

  • // 调整正文格式
  • def novel_format(novel):
  • novel = str(novel).replace('</p>','\n')
  • novel_Format = novel.replace('<p>',' ')
  • return novel_Format

2.5输出到txt

最后一步是将抓取的章节名和正文写入txt。因为前面已经把方法都写清楚了,到了这一步就轻松多,但是也要注意代码运行的逻辑性。

再是对txt文件进行读写操作时,要注意下打开方式,采用追加方式打开。

也可以自己对文件进行一些调整,比如在每章的正文后面加入一些分割符,或者是两个换行,诸如此类,使排版更清晰一些。

2.6避免被识别成恶意访问

大部分小伙伴,都会遇到被网站后台识别成恶意访问的困难。

最近也和做后台的同学讨论了下,他们判断恶意访问的准则主要为:单位时间内,相同IP的多次连续访问。

我的解决办法其实很傻瓜式,就是以降低性能为代价,强制加入等待时间。

起先等待时间是可以设置成固定值的,比如1秒。但是随着爬取量的上升,后台也会识别到咱们的诡计。

所以我最终决定采用的等待时间是0.5s~1.5s之间的随机数。按照这个反侦察手段,在一定爬取数量和网站对象内,我还未失手。可能是我爬取的数据都是一些不那么重要的资料,网站也大多为盗版。

2.7完整代码

  • import os
  • import requests
  • import re
  • import time
  • import random
  • #创建目录
  • if not os.path.exists('Txt'):
  • os.mkdir('Txt')
  • #获取网页html
  • def get_htmlcode(url):
  • try:
  • urlhtml = requests.get(url)
  • htmlcode = urlhtml.content
  • # path = open('Txt/html.txt','wb')
  • # path.write(htmlcode)
  • # path.close()
  • except:
  • htmlcode = 'error'
  • return htmlcode
  • #获取章节地址列表(无https://www.ddxs.cn前缀)
  • def get_chapter_list(htmlcode):
  • htmldecode = htmlcode.decode()
  • a = htmldecode.index('章节目录')
  • htmldecode_final = htmldecode[a:]
  • reg = r'<a href="(.+?)" title="八荒凤鸣'
  • reg_msg = re.compile(reg)
  • chapter_list = reg_msg.findall(htmldecode_final)
  • return chapter_list
  • #获取章节名称
  • def get_subject(htmlcode):
  • reg = '<h1>(.+?)</h1>'
  • reg_msg = re.compile(reg)
  • subject = reg_msg.findall(htmlcode.decode())
  • return subject
  • #获取正文
  • def get_novel(htmlcode):
  • reg = 'class="content"><p>(.+?)</p></article>'
  • reg_msg = re.compile(reg)
  • novel = reg_msg.findall(htmlcode.decode())
  • # print(novel)
  • return novel
  • #调整小说格式
  • def novel_format(novel):
  • novel = str(novel).replace('</p>','\n')
  • novel_Format = novel.replace('<p>',' ')
  • return novel_Format
  • url_list = 'https://www.ddxs.cn/book/195261/'
  • #获取章节列表的地址
  • if __name__ =='__main__':
  • #小说列表网站的html
  • Htmlcode_url_list = get_htmlcode(url_list)
  • if Htmlcode_url_list == 'error':
  • print('~~~~~~~~~~~~~网页出错了,请稍后再试~~~~~~~~~~~~~')
  • else:
  • #从源码中匹配出所有章节的地址
  • Chapter_list = get_chapter_list(Htmlcode_url_list)
  • flag = 1
  • path = open('Txt/八荒凤鸣.txt', 'a')
  • path.write('~~~~~~八荒凤鸣~~~~~~')
  • path.write('\n\n\n\n')
  • #循环处理每个章节地址
  • for a in Chapter_list:
  • #逐一处理章节地址,加上前缀
  • url_chapter = 'https://www.ddxs.cn'+a
  • try:
  • # 获取具体章节的html
  • Htmlcode = get_htmlcode(url_chapter)
  • # 从章节的html中得到章节名
  • Subject = get_subject(Htmlcode)
  • # 从html中得到小说具体内容并优化格式
  • Novel = get_novel(Htmlcode)
  • Novel_Format = novel_format(Novel)
  • # 输入到最终TXT中
  • path.write(str(Subject))
  • path.write(str(Novel_Format))
  • path.write('\n\n')
  • #加一个提示信息
  • print('小说八荒凤鸣第%d章节输出完成'%flag)
  • flag += 1
  • #随机休息0.5到1.5秒,防止被网站后台封禁
  • time.sleep(round(random.uniform(0.5,1.5),2))
  • except:
  • print('第%d章输出失败,网络错误,请稍后再试'%flag)
  • path.close()
  • print('小说爬取完成')

我在程序中加了一些网络异常的分支设计,保证整个程序的健壮性,毕竟是盗版网站,服务器不稳定。

可能是因为我是测试出身,比较在乎程序的友善性反馈。

有兴趣的小伙伴,可以试试将顶点小说网首页出现book id的所有小说数据都爬下来。

在这里插入图片描述

写在最后

不管是爬取png,还是爬取txt,原则上都是换汤不换药。爬虫也不是一种很复杂的知识。掌握精要后,只要是浏览器可以查看的数据,爬虫都可以弄下来。

方便获取更多学习、工作、生活信息请关注本站微信公众号城东书院 微信服务号城东书院 微信订阅号
推荐内容
相关内容
栏目更新
栏目热门