( 原文地址:https://0xffff.one/d/653 )

好像好久没水技术类的贴了

有看一人之下的吗?首先这部漫画几乎是被腾讯漫画垄断了的,但是在google搜索时发现排第一的居然不是腾讯😅,于是就有了爬下来的念头了。(腾讯的哪敢动啊,PS:还是建议支持正版)

找到的目标网站是这个,有做防爬,而且做得比较有意思,就是做了防爬但是什么用都没有的那种。。。

爬的过程是这样的:把每一章链接弄到手 -> 把一章里的图片的链接弄到手 -> 下载图片。

首先第一步是把每一章的链接弄到手,可以在这个页面搞,把页面拉下来后用pyquery(或者有其他方法,只是我习惯pyquery)匹配"div>ul>li>a",就是div元素里的ul元素里的li元素里的a元素(就是套娃),因为a元素有很多所以就多套了几层,如果还是不放心的话可以加个href是"/manhua/2/"开头的条件。在实践过程中发现pyquery有个问题(也有可能是我的问题?)是如果拉下来的html页面太长的话会把后面一部分截掉,所以可能需要先手动把前面不重要的部分截一下再用pyquery做匹配。

然后是搞图片链接,网站在这步做了防爬,把图片链接编成base64了,比如这样:

把base64解开后是几个函数,然后通过eval执行这些函数来生成图片的链接。解开后的函数大概长这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
(function(p, a, c, k, e, d) {
e = function(c) {
return c.toString(36)
}
;
if (!''.replace(/^/, String)) {
while (c--) {
d[c.toString(a)] = k[c] || c.toString(a)
}
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
}('6 9=[\'2/0/0/3/c.1\',\'2/0/0/3/d.1\',\'2/0/0/3/a.1\',\'2/0/0/3/8.1\',\'2/0/0/3/4.1\',\'2/0/0/3/5.1\',\'2/0/0/3/7.1\',\'2/0/0/3/b.1\',\'2/0/0/3/o.1\',\'2/0/0/3/l.1\',\'2/0/0/3/e.1\',\'2/0/0/3/m.1\',\'2/0/0/3/n.1\',\'2/0/0/3/k.1\',\'2/0/0/3/j.1\',\'f/g/h/i.1\']', 25, 25, '04|jpg|2020|10|05855418ad|059b6048da|var|05eaea7bf0|056c1761d7|packedarr|05133c4049|054a5aa050|0524cb5408|0532c104ec|0570f9a2c7|2018|12|30|c8|05009bbf55|0525a1930c|051d07517c|05465fccf6|052f44529d|0582bbd68d'.split('|'), 0, {}))

做一下简单的逆向就大概知道在做什么了,主要关注下面有两个很长的字符串参数,第一个像“2/0/0/3/c.1”这样的是匹配格式,其中的符号‘/’和符号‘.’是不动的,可以认为是一种链接的格式;其中的数字和英文字母可以认为是一种下标(英文的话a是10,b是11,以此类推),这个下标是在第二个长字串里拿东西,把里面的‘|’拿掉后就分割出一个字符串数组,比如上面下标2的话是“2020”,下标0是“04”,然后把匹配格式里的下标全部换成对应值就拿到图片链接了,比如第一个图片是:“2020/04/04/10/0524cb5408.jpg”,在前面拼上域名地址,完整链接是:“http://mh.jiduo.cc/2020/04/04/10/0524cb5408.jpg”

最后下载用requests就好了,存文件方式可以按个人喜好,比如我是在一个asset文件夹里按顺序用数字命名。。。

完整脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
#!/usr/bin/env python3
# -*- coding: UTF-8 -*-

# 爬虫核心
from pyquery import PyQuery as pq
import os,requests

# 数据处理
import json
import math
import re
import base64

agent = 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Ubuntu Chromium/75.0.3770.142 Chrome/75.0.3770.142 Safari/537.36'
headers = {'user-agent': agent}
targetDir = os.path.join(os.path.dirname(os.path.abspath(__file__)),'asset')
if not os.path.isdir(targetDir):
os.mkdir(targetDir)

url1 = 'http://m.taduo.net'
url2 = '/manhua/2/'
resp = requests.get(url1+url2,headers=headers)
doc = pq(resp.content[1000:]) # pyquery has a max length ?
#lst = doc('li>a[href^=%s]' % url2.replace('/', '\/'))
lst = doc('ul>li>a[href^=%s]' % url2.replace('/', '\/'))
urls = []
for l in lst.items():
urls.insert(0, url1+l.attr('href'))
#print(l.attr('href'))
#print(urls[:10])

def getIndex(c):
d = ord(c)
if d<60: return d-48 # digit
else: return d-87 # alpha

def decode(data64):
data = base64.b64decode(data64).decode('utf-8').split('=[\\\'')[1].split('\'.split')[0].split('\\\']\',')
encoded = data[0].split('\\\',\\\'')
dictionary = data[1].split('\'')[1].split('|')
result = []
for e in encoded:
enc = e.split()
res = ''
for ec in enc:
for c in ec:
if c=='': continue
if c=='/' or c=='.':
res += c
else:
res += dictionary[getIndex(c)]
result.append(res)
return result

chapter = 1 # start from 1
start = chapter-1 # start index
#for url in urls:
for url in urls[start:]:
print('Doing: %d | %s' % (chapter,url))
resp = requests.get(url,headers=headers)
doc = pq(resp.content)
data64 = str(resp.content).split('cp="')[1].split('";')[0]
pages = decode(data64)
#print(pages)

# save
targetDir = os.path.join(os.path.dirname(os.path.abspath(__file__)),'asset/'+str(chapter))
if not os.path.isdir(targetDir):
os.mkdir(targetDir)
# get
for p in range(len(pages)):
purl = 'http://mh.jiduo.cc/' + pages[p]
print(' - getting -> book:'+str(chapter)+' - page:'+str(p).rjust(4,'0'))
tc = 0
while True:
try:
resp = requests.get(purl,headers=headers,timeout=10)
break
except:
print('- timeout: %d' % tc)
tc += 1
if tc>10:
print('- network error -')
exit()

try:
doc = pq(resp.content)
except:
print('- error and ignore -')
continue
if '404' in doc('title').text():
print(' - 404 - ')
break
img_name = 'asset/'+str(chapter)+'/'+str(p).rjust(4,'0')+'.jpg'
fw = open(img_name,'wb')
fw.write(resp.content)
fw.close()
chapter += 1

爬下来后发现在里面插了些奇奇怪怪的广告,然后他是从腾讯哪里爬下来的,所以又有腾讯的广告😅,写了个去广告的脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#!/usr/bin/env python
import os
import hashlib

md5Ads = [
'a51bbe4a1e2e811c4ea6009f866ef8e3',
'82d1dc9e1d32cf1d8a124d4542d0b240'
]

g = os.walk('./asset/')

files = []
for path,dir_list,file_list in g:
for file_name in file_list:
files.append(os.path.join(path, file_name) )

for file in files:
with open(file, 'rb') as f:
md5 = hashlib.md5(f.read()).hexdigest()
if md5 in md5Ads:
os.remove(file)
print('deleted: %s' % file)

最后,仅供学习参考,还是要善待一下别人的服务器的🤐(侵删