用python爬取整部漫画
契机
最近突然少女心爆棚,追上了一本甜甜的恋爱日漫《寄宿学校的朱丽叶》,可是腾讯坑啊,从第五话就开始收费了
然后就开始在百度上找资源,发现某免费漫画网站好像挺容易爬的,刚好学学爬虫(虽然最后发现好像并没有我想的那么简单,但好歹还是完成了)
一直知道有 requests urllib BeautilfulSoup 这几个库,但是只会用 requests 发个不带参数的 get 请求然后打印内容啥的
因为是学习,为了给自己降低难度,所以只用熟悉一点的官方库 requests,虽然其他两个好像更加方便
收集信息
目录页
http://comic3.ikkdm.com/comiclist/2160/
网站结构很清晰,comiclist 代表所有漫画的目录,2160 为次漫画,如果要爬取所有漫画,则要控制 2160
按 F12 查看源码
第一个链接是主链接,后面三个是备用链接,备用链接不管,只需要收集两个信息,第一个 href 里的内容,以及中文名称,其中 55245 代表为第一话,因为到了后面就没规律了,所以放弃直接拼接 URL ,而是继续爬出所有链接
这里有两个坑
- 一个是网页的编码
涉及到三个编码,一个是 .encoding 返回的 ISO-8859-1,一个是本地默认的 UTF-8,一个是网页的编码 gbk,一开始我以为是应为 UTF-8 显示不正常,把它 decode 为 ISO-8895-1 ,结果更乱了,最后才知道,好像是传过来的是 ISO-8859-1,要 decode 为 gbk 才正常
#所以这两种是可以正常显示中文的
data.content.decode('gbk')
data.text.encode('ISO-8859-1').decode('gbk') #ISO-8859-1要不要好像都没关系
- 然后是,F12的内容跟爬取的内容有一点点出入(可能是浏览器做了优化???)
首先是 href 标签用的是 单引号,浏览器显示的 双引号,还有 a标签,有的是大写,有的是小写
经测试,下面的正则可以正确得到想要的内容(正则不太会写,有更好的写法,麻烦师傅们不吝赐教)
两个括号中的内容即所需,其中 1.htm 后面发现为页数,所以不在爬取的范围内
href = re.findall("<[aA] href='(/comiclist/2160/.*?)/1.htm'.*?>(.*?)</[Aa]>", data)
内容页
点击任一话,按 F12 查看源码
可以看到有一个 img 标签后面有图片地址,一开始我以为就这么简单,但是我还是太年轻了,结果测试的时候发现正则匹配不到
查看 python 返回的内容(写到这里,我想到Ctrl+U应该是一样的,貌似确实如此),发现并没有那么一句,然后我猜测可能是 JS 代码加载出来的
对比 JS 和下面的地址
<IMG SRC='"+m201304d+"newkuku/2016/06/21/寄宿学校的朱丽叶/001/000150P.jpg
http://s4.kukudm.com/newkuku/2016/06/21/寄宿学校的朱丽叶/001/000150P.jpg
发现是可以通过 JS 代码拼接出图片地址的
但是,奇怪的事情又发生了
正则匹配不到 <IMG SRC=’"+m201304d+ ,我在 notepad 里直接复制都搜索不到它,这是我一直想不通的问题 (我真是太蠢,+ 用 \ 转义便可了)
最后通过如下正则得到图片地址
image = re.search("(newkuku/20.*?)'>", content)
开始爬取
目录页
首先回到目录页,爬取每一话的链接和名称
import requests
import re
url = 'http://comic3.ikkdm.com/comiclist/2160/'
data = requests.get(url)
data = data.content.decode('gbk')
href = re.findall("<[aA] href='(/comiclist/2160/.*?)/1.htm'.*?>(.*?)</[Aa]>", data)
a = [i[0] for i in href]
b = [i[1] for i in href]
urls = ["http://comic3.ikkdm.com" + link for link in a]
paths = [d for d in b]
print(href)
print(urls)
print(paths)
输出结果,分别得到所有话的链接和名称
内容页
既然得到了每一话的链接,那么下一步就是保存图片
先测试其中一话,将图片保存到相应的目录下面,代码如下
import os
import requests
import re
url = 'http://comic3.ikkdm.com/comiclist/2160/52245/1.htm'
path = "朱丽叶"
content = requests.get(url)
content = content.content.decode('gbk')
image = re.search("(newkuku/20.*?)'>", content) #满足正则的有两个,但是只要第一个,search匹配到就结束,速度应该会比 finfall 快一点
img = 'http://s4.kukudm.com/' + image.group(1)
jpg = requests.get(img)
jpg = jpg.content
os.system('mkdir '+path)
with open(path + '/1.jpg', 'wb') as f:
f.write(jpg)
成功在脚本的目录下生成了 ”朱丽叶“ 的目录,并保存了一张 1.jpg 的图片
这还不够,我们之前提到最后的 1.htm 表示此话的第一页,这里是有规律的,后面的就链接就很容易想到,写个循环就能解决的
但是到底该循环几次呢??
再去源码里找找
发现这个东西
页眉、页脚都有,正则同样能匹配两次,所以依然用 search,不用 findall
代码如下:
import requests
import re
url = 'http://comic3.ikkdm.com/comiclist/2160/52245/1.htm'
data = requests.get(url)
data = data.content.decode('gbk')
total = re.search('共(\\d+)页', data)
print(int(total.group(1))) #输出50,没有问题,因为是循环用到的,所以把他转化为整型
完整代码
把上面的代码稍作修改、拼接到一起,便可以得到完整代码去爬取漫画了
如下
import re
import time
import requests
import os
url = 'http://comic3.ikkdm.com/comiclist/2160/'
data = requests.get(url)
data = data.text.encode('ISO-8859-1').decode('gbk')
href = re.findall("<[aA] href='(/comiclist/2160/.*?)/1.htm'.*?>(.*?)</[Aa]>", data)
a = [i[0] for i in href]
b = [i[1] for i in href]
urls = ["http://comic3.ikkdm.com" + link for link in a]
paths = [d for d in b]
for i in range(0,len(a)):
paths[i] = paths[i].replace(" ", "_")
os.system('mkdir ' + paths[i])
# url = 'http://comic3.ikkdm.com/comiclist/2160/68767'
url = urls[i]
content = requests.get(url + '/1.htm')
if content.status_code == 404:
print(paths[i]+"--->404")
continue
content = content.text.encode('ISO-8859-1').decode('gbk')
total = re.search('共(\\d+)页', content)
for j in range(1, int(total.group(1)) + 1):
time.sleep(2)
host = url + '/' + str(j) + '.htm'
content = requests.get(host)
content = content.text.encode('ISO-8859-1').decode('gbk')
image = re.search("(newkuku/20.*?)'>", content)
img = 'http://s4.kukudm.com/' + image.group(1)
jpg = requests.get(img)
jpg = jpg.content
with open(paths[i] + '/' + str(j) + '.jpg', 'wb') as f:
f.write(jpg)
print("finished %d/%d---->%d/%d" % (int(i),int(len(a)),int(j),int(total.group(1))))
实际测试过程中还是状况百出,所以又加了一点点东西
- paths[i] = paths[i].replace(" ", “_”)
中间有个空格,直接给我创建了两个文件夹,所以把空格替换一下 - 判断是不是 404
有几话没有,不加这个程序就会报错 - sleep(2)
间隔两秒,太快了容易出事 - 最后加了个进度
记录一下自己所学到的知识,以加深印象
如果这篇文章让你觉得有一点点用,那我都觉得十分荣幸