公眾號由於私人原因差不多一個月沒有更新了,首先還是得感謝沒有你們,沒有取關我,很開心。其次我會在以下的兩個月時間為大家輸出高質量的文章,讓你們能學到東西的同時,也可以讓我自己得到提升。好了,閒話不多說,開始正文!
本次文章是寫如何應對複雜點的ajax請求,上篇文章簡單寫了下簡單點的ajax請求,也就10行程式碼就可以把資料都抓下來了,可以說非常強大。有興趣的可以看看談談如何抓取ajax動態網站。
本次需要用到的工具是charles工具進行抓包。這個工具大家自行百度下載,不會用的話也可以百度下,本篇文章就不對這個工具多說了。用這個工具是它有強大的搜尋功能,可以一鍵搜尋出我想要的網路請求。
這次網站是這個http://drugs.dxy.cn/
需求是獲取所有藥品的詳細資訊,第一眼看上去是覺得不難的,點了進去你才知道是有坑的。
比如上面這個,需要獲取詳細資訊就需要模擬點選那個三角形按鈕,就需要通過傳送ajax請求來進行獲取了。
而且在你點開的時候還需要進行登陸才可以獲取全部資訊,所以這就多了一步,我們需要先模擬登陸成功後才可以進行aax請求來獲取資訊,要不然沒用。
1.模擬登陸
登陸是在這個網址上進行登陸https://auth.dxy.cn/accounts/login?service=http%3A%2F%2Fdrugs.dxy.cn%2Flogin.do%3Fdone%3Dhttp%253A%252F%252Fdrugs.dxy.cn%252Fdrug%252F54565%252Fdetail.htm
可以看到需要驗證碼,不過問題不大,可以解決。這個時候我們需要開啟開發者工具,按下f12即可,然後點選持續日誌,進行登陸,可以看到下面的圖
拉下到fromdata可以看到下面資料
經過多次測試,username和password是登陸賬號和密碼,validatecode是驗證碼,nlt是一個加密引數,由js載入而來的,其他的都是不變的。由於nlt引數是由js載入的,這就需要用到charles工具了。
登陸成功之後很容易就可以在charles找到這個請求,讓我們先看看nlt引數是怎樣來的。
我們可以先複製這個nlt引數,然後在charles工具內按下ctrl+f就會出現這個頁面
把那兩個勾選上,然後把nlt引數內容填上,點選find就會看到下面的內容,下面就是產生nlt引數的地方,點選進去,可以看到下面內容。
nlt引數是在html裡面就提供的了,這就不需要去解析js了,這就相對容易點,再看看請求網址
請求網址可以看到是和登陸網址一樣的,說明nlt引數直接提供的了,我們只需要使用正則來將nlt引數提取出來即可,現在看看驗證碼是在哪個請求產生的就可以了。
看到上面這些就知道驗證碼怎樣來了,直接使用get請求對那個網址即可。
分析完了,接下來就是程式碼部分。
2.使用python來模擬登陸
def __get_nlt(self):
"""給ult引數"""
url = "https://auth.dxy.cn/accounts/login?service=http%3A%2F%2Fdrugs.dxy.cn%2Flogin.do%3Fdone%3Dhttp%253A%252F%252Fdrugs.dxy.cn%252Fdrug%252F89790%252Fdetail.htm&qr=false&method=1"
response = self.session.post(url)
nlt = re.findall('nlt" value="([^"]+?)"', response.text) # 匹配nlt引數
return nlt
def __login(self):
"""登陸網站"""
# 登陸需要提交的表單
print('進行登陸')
data = {'username': 13060629578, # 賬號
'password': 'asd12678', # 密碼
'loginType': '1',
'validateCode': self.__get_chapter(), # 驗證碼
'keepOnlineType': '2',
'trys': '0',
'nlt': self.__get_nlt(), # 獲取nlt引數
'_eventId': 'submit'}
# 登陸網址
url = 'https://auth.dxy.cn/accounts/login?service=http%3A%2F%2Fdrugs.dxy.cn%2Flogin.do%3Fdone%3Dhttp%253A%252F%252Fdrugs.dxy.cn%252Fdrug%252F89790%252Fdetail.htm&qr=false&method=1'
response = self.session.post(url, headers=self.headers, data=data) # 請求登陸
if 'dxy_zbadwxq6' in response.text: # 此處填寫你的使用者名稱字,用於驗證是否登陸成功
print('登陸成功')
else:
print('登陸失敗,正在嘗試重新登陸')
self.__login()
def __get_chapter(self):
"""獲取驗證碼"""
try:
url = 'https://auth.dxy.cn/jcaptcha'
response = self.session.get(url, headers=self.headers)
# 儲存驗證碼
with open('code.jpg', 'wb')as f:
f.write(response.content)
im = Image.open('code.jpg')
im.show()
valide_code = input('輸入驗證碼')
return valide_code
except Exception as e:
"""驗證碼失敗,再次獲取"""
print(e)
self.__get_chapter()複製程式碼
這是部分重要程式碼,裡面都有註釋,就不多說了,進入重要部分吧。
3.分析ajax請求
登陸成功之後。隨便點進一個頁面都可以然後點選三角形按鈕都可以看到詳細內容
我們繼續使用charles工具來進行抓包,先對charles剛才抓的 包進行清理,然後點選頁面三角形按鈕來進學校獲取資訊
可以看到上面的這個請求 ,資料都是Unicode編碼的,我們需要轉,其實可以直接複製然後到命令列視窗進行列印即可,就可以看到這個就是我們想要的詳細資料
接著看下請求方式和其他需要的東西
可以看到,是個post請求,成功狀態碼是200,有很多引數,經過多次測試後,發現下面箭頭所指的五個引數都是會變化的,第一個為藥品id,第二個可以通過抓包發現(和上面獲取nlt引數都是一樣的方法)是經過js來載入的,注意,要想在charles裡面載入js要先把瀏覽器的快取先清除掉才行,要不然不會載入出來,抓包是抓不到的哈。第三個變的也是藥品的id,第四個就是通過藥品頁面載入出來的,而最後的batchId的起始id是2,之後每獲取一個詳細的內容該引數就加1。
好了,需要的內容都分析出來了,最後就是剩下實現部分了。
4.用程式碼來進行ajax請求
這個是獲取藥品的頁面內容的
def __get_content(self, item, href):
"""獲取需要提取的資訊"""
param0 = int(re.findall('\d+', item)[0])
href_id = re.findall('\d+', href)[0]
html = self.__get_html(item) # 獲取藥品的html資訊
name = re.findall('fl commend">(.*?)<', html, re.S)[0].replace('\n', '').replace('\t', '')
batchId = 2 # 初始化id,提交表單需要的,起始數字是2
id = "20A548B2C7B5F05093DFD2C71F112EEE" # scriptSessionId中的加密需要用到的資料
scriptSessionId = id + str(int(random() * 1000)) # 獲取詳情頁需要的表單資料
soup = BeautifulSoup(html, 'lxml') # 使用bs4解析
content_dd = soup.find_all('div', id='container')[0].find_all('dl')[0].find_all('dd', style=False) # 獲取整個頁面的所有資料
content_dt = soup.find_all('div', id='container')[0].find_all('dl')[0].find_all('dt') # 獲取資料的型別,就是比如適應症
keys = re.findall('<span.*?>(.*?):<', str(content_dt), re.S) # 清洗資料
values = [] # 儲存所有清洗後的資料
for i in content_dd: # 獲取所有的資料,並進行清洗
if '... ' in i.get_text(): # 這個證明資料不完整,需要進行點選
param = re.search('id="([0-9]+?)_([\d]+?)"', str(i))
# 獲取相關的表單資料
param1 = param.group(2)
# 獲取詳情內容
data_content = detail = self.__get_detail(scriptSessionId, param0, param1, batchId)
if 'img' in detail: # 判斷是否有圖片連結
data_content = ''
for x in re.split('<img.*?/>', detail, re.S):
data_content += x
# 找圖片連結
src = re.findall("<img src='([^']+?)'", detail, re.S)
for s in src:
data_content += s+' '
data_content = self.dr.sub('', data_content).replace('\n', ' ').replace('\t', ' ')
values.append(data_content)
batchId += 1
else:
if '商品名稱' in i.get_text():
con = str(i)
else:
con = self.dr.sub('', i.get_text().strip().replace('\n', ' ').replace('\t', ' '))
values.append(con)複製程式碼
這個是獲取頁面中需要進行ajax的請求的方法
def __get_detail(self, scriptSessionId, param0, param1, batch_id):
"""獲取那些需要點選才能看到的所有資料,就是模擬點選"""
data = {'callCount': 1,
'page': '/drug/%s/detail.htm' % param0, # 這個引數是藥品id
'httpSessionId': '',
'scriptSessionId': scriptSessionId, # 獲取對應的引數
'c0-scriptName': 'DrugUtils',
'c0-methodName': 'showDetail',
'c0-id': '0',
'c0-param0': 'number:%s' % param0, # 這個引數是藥品id
'c0-param1': 'number:%s' % param1, # 這個引數是需要獲取想想頁面的id
'batchId': batch_id
}
# 請求頭
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.140 Safari/537.36',
'content-type': 'text/plain'}
# 傳送請求
r = self.session.post('http://drugs.dxy.cn/dwr/call/plaincall/DrugUtils.showDetail.dwr', headers=headers, data=data)
# 把返回的html編碼並找出相對應的資料
detail = re.findall('"(.*?)"', r.text.encode('utf-8').decode("unicode-escape"), re.S)[0].strip()
return detail複製程式碼
好了,就是這麼多了!上面重要的不是程式碼,而是思想,只要你的思路跟上了,別的什麼ajax請求都是這樣子的,所以爬蟲沒什麼難的,分析這些ajax請求主要還是怕遇到加密引數,需要解析那些混淆js,這些自然而然就是爬蟲的主要部分了,做爬蟲的主要還是想辦法避開這些東西。
最後
看到這裡的一般都是真愛粉了,首先還是得感謝你們得支援哈!!!如果覺得文章對你有用,不妨點個贊,留個言,轉個發哈。這就是對我最大的鼓勵。
推薦文章
日常學python
程式碼不止bug,還有美和樂趣