本文分析http://ggzy.zwfwb.tj.gov.cn/网站
展示的是url加密(列表页源码中对应的url和详情页对应的url不同)
对应的详情页url是http://ggzy.zwfwb.tj.gov.cn/jyxxcggg/Dn+BswYsE55CV9r1AJrIgg.jhtml
梳理本案例中的反爬策略:
1.列表页响应的源码包含详情页的地址。
2.点击详情页链接,转跳加密,出现新的地址。
3.原地址直接报403访问问题。
4.用简单的静态搜索没有找到有用的生成逻辑,通过Initiator堆栈调式也没有找到有用的生成逻辑。
解决方法:
跳转加密逻辑,不是请求逻辑,所以它的加密逻辑一定是在前端 JS 文件中实现的(经验问题),此时我们可以基于网页元素事件绑定,跟踪加密逻辑。
新手可以先观察DOM事件监听是怎样的。
(在开发者工具中,直接检查 DOM 元素的所有事件,其中重点关注的是单击操作(因为基于上文分析,是单击之后跳转地址发生了变化。)
观察下面几张图
点击 第一张图中对应的VM 之后,直接跳转到 JS 代码页面,将源码格式化之后,发现非常短小。
发现加密是AES
通过断点调试知道密钥是s
修改对应的js
// npm install crypto-js
var CryptoJS = require("crypto-js");
function lx(hh) {
var aa = hh.split("/");
var aaa = aa.length;
var bbb = aa[aaa - 1].split('.');
var ccc = bbb[0];
var cccc = bbb[1];
var r = /^\+?[1-9][0-9]*$/;
var srcs = CryptoJS.enc.Utf8.parse(ccc);
var s = "qnbyzzwmdgghmcnm";
var k = CryptoJS.enc.Utf8.parse(s);
var en = CryptoJS.AES.encrypt(srcs, k, {
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7
});
var ddd = en.toString();
ddd = ddd.replace(/\//g, "^");
ddd = ddd.substring(0, ddd.length - 2);
var bbbb = ddd + '.' + bbb[1];
aa[aaa - 1] = bbbb;
var uuu = '';
for (i = 0; i < aaa; i++) {
uuu += aa[i] + '/'
}
uuu = uuu.substring(0, uuu.length - 1);
return uuu;
}
python调用js
import execjs
js = '''
// npm install crypto-js
var CryptoJS = require("crypto-js");
function lx(hh) {
var aa = hh.split("/");
var aaa = aa.length;
var bbb = aa[aaa - 1].split('.');
var ccc = bbb[0];
var cccc = bbb[1];
var r = /^\+?[1-9][0-9]*$/;
var srcs = CryptoJS.enc.Utf8.parse(ccc);
var s = "qnbyzzwmdgghmcnm";
var k = CryptoJS.enc.Utf8.parse(s);
var en = CryptoJS.AES.encrypt(srcs, k, {
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7
});
var ddd = en.toString();
ddd = ddd.replace(/\//g, "^");
ddd = ddd.substring(0, ddd.length - 2);
var bbbb = ddd + '.' + bbb[1];
aa[aaa - 1] = bbbb;
var uuu = '';
for (i = 0; i < aaa; i++) {
uuu += aa[i] + '/'
}
uuu = uuu.substring(0, uuu.length - 1);
return uuu;
}
'''
hh="http://ggzy.zwfwb.tj.gov.cn:80/jyxxcgjg/970369.jhtml"
url = execjs.compile(js).call('lx',hh)
print(url)
可以将对应的js修改成python
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
import base64
def encrypt_text(text, key):
aes = AES.new(key.encode('utf-8'), AES.MODE_ECB)
encrypt_str = aes.encrypt(pad(text.encode('utf-8'), AES.block_size, style='pkcs7'))
encrypt_str = str(base64.encodebytes(encrypt_str), encoding='utf-8')
return encrypt_str
if __name__ == '__main__':
key = 'qnbyzzwmdgghmcnm'
es = encrypt_text("1013331", key)
es.replace("\n", "") # 去掉后面的换行
es = es.replace('/', '^')[:-2] # 去掉最后的 ==
print(es)
完整代码
完整获取url,加密url,获取详情页数据
import requests,re
from bs4 import BeautifulSoup
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
import base64
def get_urlid():
list_urls = []
for i in range(1,2):
list_url=[]
if i==1:
url = 'http://ggzy.zwfwb.tj.gov.cn/jyxx/index.jhtml'
else:
url = 'http://ggzy.zwfwb.tj.gov.cn/jyxx/index_{}.jhtml'.format(i)
r = requests.get(url).text
soup = BeautifulSoup(r, 'lxml')
books = soup.find_all(class_='article-list3-t')
# print(books)
for book in books:
url_list = book.find(name='a')['url']
list_url.append(url_list)
list_urls.append(list_url)
return list_urls
def encrypt_text(text, key):
aes = AES.new(key.encode('utf-8'), AES.MODE_ECB)
encrypt_str = aes.encrypt(pad(text.encode('utf-8'), AES.block_size, style='pkcs7'))
encrypt_str = str(base64.encodebytes(encrypt_str), encoding='utf-8')
return encrypt_str
def get_content(url,url_id):
r = requests.get(url).text
soup = BeautifulSoup(r, 'lxml')
books = soup.find(class_='content-article')
# print(books)
contents = books.find(id='content').find_all(name='p')
s=''
for content in contents:
con = content.text
s=s+'\n'+con
print(con)
with open('{}.txt'.format(url_id),'w',encoding='utf-8') as f:
f.write(s)
if __name__ == '__main__':
key = 'qnbyzzwmdgghmcnm'
urlids=get_urlid()
id_lists = []
for urlid in urlids:
for id_url in urlid:
id_es = []
list_id = id_url.split('/')[-1].split('.')[0]
id_es.append(list_id)
es = encrypt_text(list_id, key)
es=es.replace("\n", "") # 去掉后面的换行
es = es.replace('/', '^')[:-2] # 去掉最后的 ==
es = es.replace("^", "%5E")
id_es.append(es)
url=id_url.replace(list_id, es)
print(id_url,url)
get_content(url,list_id)