Python3 网络爬虫:漫画下载,动态加载、反爬虫这都不叫事
liuian 2025-04-26 19:24 31 浏览
一、前言
作者:Jack Cui
经过上两篇文章的学习,Python爬虫三步走:发起请求、解析数据、保存数据,已经掌握,算入门爬虫了吗?
不,还远远不够!只掌握这些,还只能算门外汉级别。
今天,就来带大家继续学习,怎么爬的更优雅!
按照惯例,还是从实战出发,今天咱们就爬个图片,盘点那些遇到的问题,和优雅的解决方案。
本文男女老少皆宜,什么妹子图、肌肉男,学会了本文的方法,一切尽收囊中!
私信小编001即可获取大量Python学习资料!
二、实战背景
咱不来吸睛劲爆的图片下载,咱来点清淡的家常菜。
动漫之家漫画下载!
这个实战,你会遇到动态加载、初级反爬,会了本文的方法,你还怕爬不到心心念的"美图"吗?
三、漫画下载
咱不下载整站资源,就挑一本下载,别给服务器太大压力。
挑来挑去,找了本动漫之家排名靠前的一本《妖神记》,说实话,看了漫画第一章的内容,浓浓的火影气息。
URL:https://www.dmzj.com/info/yaoshenji.html
想下载这本动漫,我们需要保存所有章节的图片到本地。我们先捋捋思路:
- 拿到所有章节名和章节链接
- 根据章节链接章节里的所有漫画图片
- 根据章节名,分类保存漫画
看似简单,实际做起来,可能遇到各种各样的问题,让我们一起优雅的解决这些问题吧!
1、获取章节名和章节链接
一个网页,是由很多div元素组成的
不同的div存放不同的内容,如上图,有存放标题Jack Cui的div,有存放菜单的div,有存放正文内容的div,有存放版权信息的div。
瞧,不难发现,只要拿到class属性为zj_list的div标签,就能拿到章节名和章节链接,都存放在这个div标签下的a标签中。
再仔细观察一番,你会发现,div标签下还有个ul标签,ul标签是距离a标签最近的标签。
用上一篇文章讲解的BeautifulSoup,实际上直接匹配最近的class属性为list_con_li的ul标签即可。编写如下代码:
Python
import requests
from bs4 import BeautifulSoup
target_url = "https://www.dmzj.com/info/yaoshenji.html"
r = requests.get(url=target_url)
bs = BeautifulSoup(r.text, 'lxml')
list_con_li = bs.find('ul', class_="list_con_li")
comic_list = list_con_li.find_all('a')
chapter_names = []
chapter_urls = []
for comic in comic_list:
href = comic.get('href')
name = comic.text
chapter_names.insert(0, name)
chapter_urls.insert(0, href)
print(chapter_names)
print(chapter_urls)瞧,章节名和章节链接搞定了!
没有难度啊?别急,难的在后面。
2、获取漫画图片地址
我们只要分析在一个章节里怎么获取图片,就能批量的在各个章节获取漫画图片。
我们先看第一章的内容。
URL:https://www.dmzj.com/view/yaoshenji/41917.html
打开第一章的链接,你会发现,链接后面自动添加了#@page=1。
你翻页会发现,第二页的链接是后面加了#@page=2,第三页的链接是后面加了#@page=3,以此类推。
但是,这些并不是图片的地址,而是这个展示页面的地址,要下载图片,首先要拿到图片的真实地址。
审查元素找图片地址,你会发现,这个页面不能右键!
这就是最最最最低级的反爬虫手段,这个时候我们可以通过键盘的F12调出审查元素窗口。
有的网站甚至把F12都禁掉,这种也是很低级的反爬虫手段,骗骗刚入门的手段而已。
面对这种禁止看页面源码的初级手段,一个优雅的通用解决办法是,在连接前加个view-source:。
Shell
view-source:https://www.dmzj.com/view/yaoshenji/41917.html用这个链接,直接看的就是页面源码。
更简单的办法是,将鼠标焦点放在浏览器地址栏,然后按下F12依然可以调出调试窗口。
这个漫画网站,还是可以通过F12审查元素,调出调试窗口的。
我们可以在浏览器调试窗口中的Network里找到这个页面加载的内容,例如一些css文件啊、js文件啊、图片啊,等等等。
要找图片的地址,直接在这里找,别在html页面里找,html信息那么多,一条一条看得找到猴年马月。
在Network中可以很轻松地找到我们想要的图片真实地址,调试工具很强大,Headers可以看一些请求头信息,Preview可以浏览返回信息。
搜索功能,过滤功能等等,应有尽有,具体怎么用,自己动手点一点,就知道了!
好了,拿到了图片的真实地址,我们看下链接:
https://images.dmzj.com/img/chapterpic/3059/14237/14395217739069.jpg
这就是图片的真实地址,拿着这个链接去html页面中搜索,看下它存放在哪个img标签里了,搜索一下你会发现,浏览器中的html页面是有这个图片链接的。
但你是用view-source:打开这个页面,你会发现你搜索不到这个图片链接。
Shell
view-source:https://www.dmzj.com/view/yaoshenji/41917.html
记住,这就说明,这个图片是动态加载的!
使用view-source:方法,就是看页面源码,并不管动态加载的内容。这里面没有图片链接,就说明图片是动态加载的。
是不是判断起来很简单?
遇到动态加载不要慌,使用JavaScript动态加载,无外乎两种方式:
- 外部加载
- 内部加载
外部加载就是在html页面中,以引用的形式,加载一个js,例如这样:
XHTML
<script type="text/javascript" src="https://cuijiahua.com/call.js"></script>这段代码得意思是,引用cuijiahua.com域名下的call.js文件。
内部加载就是Javascript脚本内容写在html内,例如这个漫画网站。
这时候,就可以用搜索功能了,教一个搜索小技巧。
https://images.dmzj.com/img/chapterpic/3059/14237/14395217739069.jpg
图片链接是这个,那就用图片的名字去掉后缀,也就是14395217739069在浏览器的调试页面搜索,因为一般这种动态加载,链接都是程序合成的,搜它准没错!
XHTML
<script type="text/javascript">
var arr_img = new Array();
var page = '';
eval(function(p,a,c,k,e,d){e=function(c){return(c<a?'':e(parseInt(c/a)))+((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--){d[e(c)]=k[c]||e(c)}k=[function(e){return d[e]}];e=function(){return'\\w+'};c=1};while(c--){if(k[c]){p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c])}}return p}('g f=\'{"e":"h","i":"0","l":"k\\/3\\/5\\/2\\/j.4\\r\\6\\/3\\/5\\/2\\/d.4\\r\\6\\/3\\/5\\/2\\/7.4\\r\\6\\/3\\/5\\/2\\/8.4\\r\\6\\/3\\/5\\/2\\/c.4\\r\\6\\/3\\/5\\/2\\/b.4\\r\\6\\/3\\/5\\/2\\/a.4\\r\\6\\/3\\/5\\/2\\/9.4\\r\\6\\/3\\/5\\/2\\/m.4\\r\\6\\/3\\/5\\/2\\/v.4\\r\\6\\/3\\/5\\/2\\/A.4\\r\\6\\/3\\/5\\/2\\/n.4\\r\\6\\/3\\/5\\/2\\/B.4\\r\\6\\/3\\/5\\/2\\/x.4\\r\\6\\/3\\/5\\/2\\/y.4","w":"p","o":"1","q":"\\s\\u \\t\\z"}\';',38,38,'||14237|chapterpic|jpg|3059|nimg|14395217891719|14395217893745|14395217913416|14395217908431|14395217904781|1439521790086|1439521788936|id|pages|var|41917|hidden|14395217739069|img|page_url|14395217918734|14395217931135|chapter_order|15|chapter_name||u7b2c01|u91cd|u8bdd|14395217923415|sum_pages|14395217940216|14395217943921|u751f|14395217926321|1439521793602'.split('|'),0,{}))
</script>不出意外,你就能看到这段代码,14395217739069就混在其中!
看不懂Javascript,怎么办啊?
没关系,说实话,我看着也费劲儿。
那咱们就找找规律,分析分析,看看能不能优雅的解决这个动态加载问题,我们再看这个图片链接:
https://images.dmzj.com/img/chapterpic/3059/14237/14395217739069.jpg
链接中的数字是不是眼熟?
这不就是这几个数字合成的吗?
好了,我有个大胆的想法!直接把这些长的数字搞出来,合成下链接试试看。
Python
import requests
from bs4 import BeautifulSoup
import re
url = 'https://www.dmzj.com/view/yaoshenji/41917.html'
r = requests.get(url=url)
html = BeautifulSoup(r.text, 'lxml')
script_info = html.script
pics = re.findall('\d{13,14}', str(script_info))
chapterpic_hou = re.findall('\|(\d{5})\|', str(script_info))[0]
chapterpic_qian = re.findall('\|(\d{4})\|', str(script_info))[0]
for pic in pics:
url = 'https://images.dmzj.com/img/chapterpic/' + chapterpic_qian + '/' + chapterpic_hou + '/' + pic + '.jpg'
print(url)运行代码,你可以得到如下结果:
踏破铁鞋无觅处,得来全不费工夫!
比对一下你会发现,这些,还真就是漫画图片的链接!
但是有个问题,这么合成的的图片链接不是按照漫画顺序的,这下载下来漫画图片都是乱的啊!不优雅!
这个网站也是人写的嘛!是人,就好办!惯性思维,要是你,是不是小数放在前面,大数放在后面?这些长的数字里,有13位的,有14位的,并且都是以14开头的数字,那我就赌它末位补零后的结果,就是图片的顺序!
Python
import requests
from bs4 import BeautifulSoup
import re
url = 'https://www.dmzj.com/view/yaoshenji/41917.html'
r = requests.get(url=url)
html = BeautifulSoup(r.text, 'lxml')
script_info = html.script
pics = re.findall('\d{13,14}', str(script_info))
for idx, pic in enumerate(pics):
if len(pic) == 13:
pics[idx] = pic + '0'
pics = sorted(pics, key=lambda x:int(x))
chapterpic_hou = re.findall('\|(\d{5})\|', str(script_info))[0]
chapterpic_qian = re.findall('\|(\d{4})\|', str(script_info))[0]
for pic in pics:
if pic[-1] == '0':
url = 'https://images.dmzj.com/img/chapterpic/' + chapterpic_qian + '/' + chapterpic_hou + '/' + pic[:-1] + '.jpg'
else:
url = 'https://images.dmzj.com/img/chapterpic/' + chapterpic_qian + '/' + chapterpic_hou + '/' + pic + '.jpg'
print(url)程序对13位的数字,末位补零,然后排序。
在跟网页的链接按顺序比对,你会发现没错!就是这个顺序!
不用读懂Javascript合成链接代码,直接分析测试,够不够优雅?
3、下载图片
万事俱备,只欠东风!
使用其中一个图片链接,用代码下载试试。
Python
import requests
from urllib.request import urlretrieve
dn_url = 'https://images.dmzj.com/img/chapterpic/3059/14237/14395217739069.jpg'
urlretrieve(dn_url,'1.jpg')通过urlretrieve方法,就可以下载,这是最简单的下载方法。第一个参数是下载链接,第二个参数是下载后的文件保存名。
不出意外,就可以顺利下载这张图片!
但是,意外发生了!
出现了HTTP Error,错误代码是403。
403表示资源不可用,这是又是一种典型的反扒虫手段。
别慌,我们再分析一波!
打开这个图片链接:
URL:https://images.dmzj.com/img/chapterpic/3059/14237/14395217739069.jpg
这个地址就是图片的真实地址,在浏览器中打开,可能直接无法打开,或者能打开,但是一刷新就又不能打开了!
如果再打开章节页面,再打开这个图片链接就又能看到图片了。
章节URL:
https://www.dmzj.com/view/yaoshenji/41917.html
记住,这就是一种典型的通过Referer的反扒爬虫手段!
Referer可以理解为来路,先打开章节URL链接,再打开图片链接。打开图片的时候,Referer的信息里保存的是章节URL。
动漫之家网站的做法就是,站内的用户访问这个图片,我就给他看,从其它地方过来的用户,我就不给他看。
是不是站内用户,就是根据Referer进行简单的判断。
这就是很典型的,反爬虫手段!
解决办法也简单,它需要啥,咱给它就完了。Python
import requests
from contextlib import closing
download_header = {
'Referer': 'https://www.dmzj.com/view/yaoshenji/41917.html'
}
dn_url = 'https://images.dmzj.com/img/chapterpic/3059/14237/14395217739069.jpg'
with closing(requests.get(dn_url, headers=download_header, stream=True)) as response:
chunk_size = 1024
content_size = int(response.headers['content-length'])
if response.status_code == 200:
print('文件大小:%0.2f KB' % (content_size / chunk_size))
with open('1.jpg', "wb") as file:
for data in response.iter_content(chunk_size=chunk_size):
file.write(data)
else:
print('链接异常')
print('下载完成!')使用closing方法可以设置Headers信息,这个Headers信息里保存Referer来路,就是第一章的URL,最后以写文件的形式,保存这个图片。
下载完成!就是这么简单!
四、漫画下载
将代码整合在一起,下载整部漫画。编写代码如下:Python
import requests
import os
import re
from bs4 import BeautifulSoup
from contextlib import closing
from tqdm import tqdm
import time
"""
Author:
Jack Cui
Wechat:
https://mp.weixin.qq.com/s/OCWwRVDFNslIuKyiCVUoTA
"""
# 创建保存目录
save_dir = '妖神记'
if save_dir not in os.listdir('./'):
os.mkdir(save_dir)
target_url = "https://www.dmzj.com/info/yaoshenji.html"
# 获取动漫章节链接和章节名
r = requests.get(url = target_url)
bs = BeautifulSoup(r.text, 'lxml')
list_con_li = bs.find('ul', class_="list_con_li")
cartoon_list = list_con_li.find_all('a')
chapter_names = []
chapter_urls = []
for cartoon in cartoon_list:
href = cartoon.get('href')
name = cartoon.text
chapter_names.insert(0, name)
chapter_urls.insert(0, href)
# 下载漫画
for i, url in enumerate(tqdm(chapter_urls)):
download_header = {
'Referer': url
}
name = chapter_names[i]
# 去掉.
while '.' in name:
name = name.replace('.', '')
chapter_save_dir = os.path.join(save_dir, name)
if name not in os.listdir(save_dir):
os.mkdir(chapter_save_dir)
r = requests.get(url = url)
html = BeautifulSoup(r.text, 'lxml')
script_info = html.script
pics = re.findall('\d{13,14}', str(script_info))
for j, pic in enumerate(pics):
if len(pic) == 13:
pics[j] = pic + '0'
pics = sorted(pics, key=lambda x:int(x))
chapterpic_hou = re.findall('\|(\d{5})\|', str(script_info))[0]
chapterpic_qian = re.findall('\|(\d{4})\|', str(script_info))[0]
for idx, pic in enumerate(pics):
if pic[-1] == '0':
url = 'https://images.dmzj.com/img/chapterpic/' + chapterpic_qian + '/' + chapterpic_hou + '/' + pic[:-1] + '.jpg'
else:
url = 'https://images.dmzj.com/img/chapterpic/' + chapterpic_qian + '/' + chapterpic_hou + '/' + pic + '.jpg'
pic_name = '%03d.jpg' % (idx + 1)
pic_save_path = os.path.join(chapter_save_dir, pic_name)
with closing(requests.get(url, headers = download_header, stream = True)) as response:
chunk_size = 1024
content_size = int(response.headers['content-length'])
if response.status_code == 200:
with open(pic_save_path, "wb") as file:
for data in response.iter_content(chunk_size=chunk_size):
file.write(data)
else:
print('链接异常')
time.sleep(10)大约40分钟,漫画即可下载完成!
还是那句话,我们要做一个友好的爬虫。写爬虫,要谨慎,勿给服务器增加过多的压力,满足我们的获取数据的需求,这就够了。
你好,我也好,大家好才是真的好。
五、总结
- 本文讲解了如何判断页面信息是不是动态加载的,如何解决动态加载问题。
- 本文讲解了一些常见的反爬虫策略以及解决办法。
相关推荐
-
- 4000台式电脑最好的组装配置
-
四千元价格组装电脑主机与五千元组装电脑主机的价格类似,因为电脑主机就几个大部件,电脑主机主板是多少代的产品?主板内存的插槽数?电脑处理器等如果是自己组装,都可以配置到十二代产品,电脑硬盘可以分为256G固态硬盘做系统盘,1T机械硬盘作为工作...
-
2025-11-06 20:05 liuian
- linux是一种什么系统(linux属于什么系统)
-
Linux,全称GNU/Linux,是一种免费使用和自由传播的类UNIX操作系统,是一个基于POSIX的多用户、多任务、支持多线程和多CPU的操作系统。其内核由林纳斯·本纳第克特·托瓦兹于1991年1...
- 手机管理大师免费版(手机管理大师极速版)
-
使用手机“文件管理”打开文件夹时提示访问受限,需要前往“文件”应用查看1.进入手机设置——安全——应用权限——权限/应用2.在手机桌面找到手机管家——权限隐私——应用权限——权限/应用?当然,相对于被...
- 电脑能开机但是进不去桌面怎么办
-
打开任务管理器按Ctrl+Shift+Esc打开任务管理器。文件中运行新任务点击文件,运行新任务。输入指令重启桌面输入explorer.exe,点击确定,等待桌面重启完成就可以了。电脑已经是我们生活中...
- 怎样解除自动关机模式(怎样解除自动开关机)
-
1、打开手机主界面,找到系统自带的“时钟”应用,点击打开它。2、点击进入时钟后,点击右下角的“计时器”。3、进入到计时器后,点击“在计时结束启用雷达”这个选项。4、然后在这里,下拉到最下面,勾选“停...
- 电脑最高配置是什么配置2025
-
一,2023最新主流电脑装机配置如下。二,处理器可以使用十二代的i512400或者i512490f,内存16gb双通道,显卡rtx3060,主板可以使用b660m或者h610m。三,如果十三代酷睿...
- MySQL慢查询优化:从explain到索引,DBA手把手教你提升10倍性能
-
数据库性能是应用系统的生命线,而慢查询就像隐藏在系统中的定时炸弹。某电商平台曾因一条未优化的SQL导致订单系统响应时间从200ms飙升至8秒,最终引发用户投诉和订单流失。今天我们就来系统学习MySQL...
- 一文读懂SQL五大操作类别(DDL/DML/DQL/DCL/TCL)的基础语法
-
在SQL中,DDL、DML、DQL、DCL、TCL是按操作类型划分的五大核心语言类别,缩写及简介如下:DDL(DataDefinitionLanguage,数据定义语言):用于定义和管理数据库结构...
- 闲来无事,学学Mysql增、删,改,查
-
Mysql增、删,改,查1“增”——添加数据1.1为表中所有字段添加数据1.1.1INSERT语句中指定所有字段名语法:INSERTINTO表名(字段名1,字段名2,…)VALUES(值1...
- 数据库:MySQL 高性能优化规范建议
-
数据库命令规范所有数据库对象名称必须使用小写字母并用下划线分割所有数据库对象名称禁止使用MySQL保留关键字(如果表名中包含关键字查询时,需要将其用单引号括起来)数据库对象的命名要能做到见名识意,...
- 下载工具合集_下载工具手机版
-
迅雷,在国内的下载地位还是很难撼动的,所需要用到的地方还挺多。缺点就是不开会员,软件会限速。EagleGet,全能下载管理器,支持HTTP(S)FTPMMSRTSP协议,也可以使用浏览器扩展检测...
- mediamtx v1.15.2 更新详解:功能优化与问题修复
-
mediamtxv1.15.2已于2025年10月14日发布,本次更新在功能、性能优化以及问题修复方面带来了多项改进,同时也更新了部分依赖库并提升了安全性。以下为本次更新的详细内容:...
- 声学成像仪:泄露监测 “雷达” 方案开启精准防控
-
声学成像仪背景将声像图与阵列上配装的摄像实所拍的视频图像以透明的方式叠合在一起,就形成了可直观分析被测物产生状态。这种利用声学、电子学和信息处理等技术,变换成人眼可见的图像的技术可以帮助人们直观地认识...
- 最稳存储方案:两种方法将摄像头接入威联通Qu405,录像不再丢失
-
今年我家至少被4位邻居敲门,就是为了查监控!!!原因是小区内部监控很早就停止维护了,半夜老有小黄毛掰车门偷东西,还有闲的没事划车的,车主损失不小,我家很早就配备监控了,人来亮灯有一定威慑力,不过监控设...
- 离岗检测算法_离岗检查内容
-
一、研发背景如今社会许多岗位是严禁随意脱离岗位的,如塔台、保安室、监狱狱警监控室等等,因为此类行为可能会引起重大事故,而此类岗位监督管理又有一定困难,因此促生了智能视频识别系统的出现。二、产品概述及工...
- 一周热门
- 最近发表
- 标签列表
-
- python判断字典是否为空 (50)
- crontab每周一执行 (48)
- aes和des区别 (43)
- bash脚本和shell脚本的区别 (35)
- canvas库 (33)
- dataframe筛选满足条件的行 (35)
- gitlab日志 (33)
- lua xpcall (36)
- blob转json (33)
- python判断是否在列表中 (34)
- python html转pdf (36)
- 安装指定版本npm (37)
- idea搜索jar包内容 (33)
- css鼠标悬停出现隐藏的文字 (34)
- linux nacos启动命令 (33)
- gitlab 日志 (36)
- adb pull (37)
- python判断元素在不在列表里 (34)
- python 字典删除元素 (34)
- vscode切换git分支 (35)
- python bytes转16进制 (35)
- grep前后几行 (34)
- hashmap转list (35)
- c++ 字符串查找 (35)
- mysql刷新权限 (34)
