python爬取Ajax動(dòng)態(tài)加載網(wǎng)頁(yè)過(guò)程解析
常見(jiàn)的反爬機(jī)制及處理方式
1、Headers反爬蟲(chóng) :Cookie、Referer、User-Agent
解決方案: 通過(guò)F12獲取headers,傳給requests.get()方法
2、IP限制 :網(wǎng)站根據(jù)IP地址訪問(wèn)頻率進(jìn)行反爬,短時(shí)間內(nèi)進(jìn)制IP訪問(wèn)
解決方案:
1、構(gòu)造自己IP代理池,每次訪問(wèn)隨機(jī)選擇代理,經(jīng)常更新代理池
2、購(gòu)買(mǎi)開(kāi)放代理或私密代理IP
3、降低爬取的速度
3、User-Agent限制 :類(lèi)似于IP限制
解決方案: 構(gòu)造自己的User-Agent池,每次訪問(wèn)隨機(jī)選擇
5、對(duì)查詢(xún)參數(shù)或Form表單數(shù)據(jù)認(rèn)證(salt、sign)
解決方案: 找到JS文件,分析JS處理方法,用Python按同樣方式處理
6、對(duì)響應(yīng)內(nèi)容做處理
解決方案: 打印并查看響應(yīng)內(nèi)容,用xpath或正則做處理
python中正則處理headers和formdata
1、pycharm進(jìn)入方法 :Ctrl + r ,選中 Regex
2、處理headers和formdata
(.*): (.*)
"1":"1":"2",
3、點(diǎn)擊 Replace All
民政部網(wǎng)站數(shù)據(jù)抓取
目標(biāo): 抓取最新中華人民共和國(guó)縣以上行政區(qū)劃代碼
URL: http://www.mca.gov.cn/article/sj/xzqh/2019/ - 民政數(shù)據(jù) - 行政區(qū)劃代碼
實(shí)現(xiàn)步驟
1、從民政數(shù)據(jù)網(wǎng)站中提取最新行政區(qū)劃代碼鏈接
最新的在上面,命名格式: 2019年X月中華人民共和國(guó)縣以上行政區(qū)劃代碼
import requests
from lxml import etree
import re
url = 'http://www.mca.gov.cn/article/sj/xzqh/2019/'
headers = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36'}
html = requests.get(url, headers=headers).text
parse_html = etree.HTML(html)
article_list = parse_html.xpath('//a[@class="artitlelist"]')
for article in article_list:
title = article.xpath('./@title')[0]
# 正則匹配title中包含這個(gè)字符串的鏈接
if title.endswith('代碼'):
# 獲取到第1個(gè)就停止即可,第1個(gè)永遠(yuǎn)是最新的鏈接
two_link = 'http://www.mca.gov.cn' + article.xpath('./@href')[0]
print(two_link)
break
2、從二級(jí)頁(yè)面鏈接中提取真實(shí)鏈接(反爬-響應(yīng)網(wǎng)頁(yè)內(nèi)容中嵌入JS,指向新的網(wǎng)頁(yè)鏈接)
向二級(jí)頁(yè)面鏈接發(fā)請(qǐng)求得到響應(yīng)內(nèi)容,并查看嵌入的JS代碼
正則提取真實(shí)的二級(jí)頁(yè)面鏈接
# 爬取二級(jí)“假”鏈接 two_html = requests.get(two_link, headers=headers).text # 從二級(jí)頁(yè)面的響應(yīng)中提取真實(shí)的鏈接(此處為JS動(dòng)態(tài)加載跳轉(zhuǎn)的地址) new_two_link = re.findall(r'window.location.href="(.*?)" rel="external nofollow" rel="external nofollow" ', two_html, re.S)[0]
3、在數(shù)據(jù)庫(kù)表中查詢(xún)此條鏈接是否已經(jīng)爬取,建立增量爬蟲(chóng)
數(shù)據(jù)庫(kù)中建立version表,存儲(chǔ)爬取的鏈接
每次執(zhí)行程序和version表中記錄核對(duì),查看是否已經(jīng)爬取過(guò)
cursor.execute('select * from version')
result = self.cursor.fetchall()
if result:
if result[-1][0] == two_link:
print('已是最新')
else:
# 有更新,開(kāi)始抓取
# 將鏈接再重新插入version表記錄
4、代碼實(shí)現(xiàn)
import requests
from lxml import etree
import re
import pymysql
class GovementSpider(object):
def __init__(self):
self.url = 'http://www.mca.gov.cn/article/sj/xzqh/2019/'
self.headers = {'User-Agent': 'Mozilla/5.0'}
# 創(chuàng)建2個(gè)對(duì)象
self.db = pymysql.connect('127.0.0.1', 'root', '123456', 'govdb', charset='utf8')
self.cursor = self.db.cursor()
# 獲取假鏈接
def get_false_link(self):
html = requests.get(url=self.url, headers=self.headers).text
# 此處隱藏了真實(shí)的二級(jí)頁(yè)面的url鏈接,真實(shí)的在假的響應(yīng)網(wǎng)頁(yè)中,通過(guò)js腳本生成,
# 假的鏈接在網(wǎng)頁(yè)中可以訪問(wèn),但是爬取到的內(nèi)容卻不是我們想要的
parse_html = etree.HTML(html)
a_list = parse_html.xpath('//a[@class="artitlelist"]')
for a in a_list:
# get()方法:獲取某個(gè)屬性的值
title = a.get('title')
if title.endswith('代碼'):
# 獲取到第1個(gè)就停止即可,第1個(gè)永遠(yuǎn)是最新的鏈接
false_link = 'http://www.mca.gov.cn' + a.get('href')
print("二級(jí)“假”鏈接的網(wǎng)址為", false_link)
break
# 提取真鏈接
self.incr_spider(false_link)
# 增量爬取函數(shù)
def incr_spider(self, false_link):
self.cursor.execute('select url from version where url=%s', [false_link])
# fetchall: (('http://xxxx.html',),)
result = self.cursor.fetchall()
# not result:代表數(shù)據(jù)庫(kù)version表中無(wú)數(shù)據(jù)
if not result:
self.get_true_link(false_link)
# 可選操作: 數(shù)據(jù)庫(kù)version表中只保留最新1條數(shù)據(jù)
self.cursor.execute("delete from version")
# 把爬取后的url插入到version表中
self.cursor.execute('insert into version values(%s)', [false_link])
self.db.commit()
else:
print('數(shù)據(jù)已是最新,無(wú)須爬取')
# 獲取真鏈接
def get_true_link(self, false_link):
# 先獲取假鏈接的響應(yīng),然后根據(jù)響應(yīng)獲取真鏈接
html = requests.get(url=false_link, headers=self.headers).text
# 從二級(jí)頁(yè)面的響應(yīng)中提取真實(shí)的鏈接(此處為JS動(dòng)態(tài)加載跳轉(zhuǎn)的地址)
re_bds = r'window.location.href="(.*?)" rel="external nofollow" rel="external nofollow" '
pattern = re.compile(re_bds, re.S)
true_link = pattern.findall(html)[0]
self.save_data(true_link) # 提取真鏈接的數(shù)據(jù)
# 用xpath直接提取數(shù)據(jù)
def save_data(self, true_link):
html = requests.get(url=true_link, headers=self.headers).text
# 基準(zhǔn)xpath,提取每個(gè)信息的節(jié)點(diǎn)列表對(duì)象
parse_html = etree.HTML(html)
tr_list = parse_html.xpath('//tr[@height="19"]')
for tr in tr_list:
code = tr.xpath('./td[2]/text()')[0].strip() # 行政區(qū)劃代碼
name = tr.xpath('./td[3]/text()')[0].strip() # 單位名稱(chēng)
print(name, code)
# 主函數(shù)
def main(self):
self.get_false_link()
if __name__ == '__main__':
spider = GovementSpider()
spider.main()
動(dòng)態(tài)加載數(shù)據(jù)抓取-Ajax
特點(diǎn)
右鍵 -> 查看網(wǎng)頁(yè)源碼中沒(méi)有具體數(shù)據(jù)
滾動(dòng)鼠標(biāo)滑輪或其他動(dòng)作時(shí)加載
抓取
F12打開(kāi)控制臺(tái),選擇XHR異步加載數(shù)據(jù)包,找到頁(yè)面動(dòng)作抓取網(wǎng)絡(luò)數(shù)據(jù)包
通過(guò)XHR-->Header-->General-->Request URL,獲取json文件URL地址
通過(guò)XHR-->Header-->Query String Parameters(查詢(xún)參數(shù))
豆瓣電影數(shù)據(jù)抓取案例
目標(biāo)
地址: 豆瓣電影 - 排行榜 - 劇情
https://movie.douban.com/typerank?
type_name=%E5%89%A7%E6%83%85&type=11&interval_id=100:90&action=
目標(biāo): 爬取電影名稱(chēng)、電影評(píng)分
F12抓包(XHR)
1、Request URL(基準(zhǔn)URL地址) :https://movie.douban.com/j/chart/top_list?
2、Query String Paramaters(查詢(xún)參數(shù))
# 查詢(xún)參數(shù)如下:
type: 13 # 電影類(lèi)型
interval_id: 100:90
action: '[{},{},{}]'
start: 0 # 每次加載電影的起始索引值
limit: 20 # 每次加載的電影數(shù)量
json文件在以下地址:
基準(zhǔn)URL地址+查詢(xún)參數(shù)
'https://movie.douban.com/j/chart/top_list?'+'type=11&interval_id=100%3A90&action=&start=20&limit=20'
代碼實(shí)現(xiàn)
import requests
import time
from fake_useragent import UserAgent
class DoubanSpider(object):
def __init__(self):
self.base_url = 'https://movie.douban.com/j/chart/top_list?'
self.i = 0
def get_html(self, params):
headers = {'User-Agent': UserAgent().random}
res = requests.get(url=self.base_url, params=params, headers=headers)
res.encoding = 'utf-8'
html = res.json() # 將json格式的字符串轉(zhuǎn)為python數(shù)據(jù)類(lèi)型
self.parse_html(html) # 直接調(diào)用解析函數(shù)
def parse_html(self, html):
# html: [{電影1信息},{電影2信息},{}]
item = {}
for one in html:
item['name'] = one['title'] # 電影名
item['score'] = one['score'] # 評(píng)分
item['time'] = one['release_date'] # 打印測(cè)試
# 打印顯示
print(item)
self.i += 1
# 獲取電影總數(shù)
def get_total(self, typ):
# 異步動(dòng)態(tài)加載的數(shù)據(jù) 都可以在XHR數(shù)據(jù)抓包
url = 'https://movie.douban.com/j/chart/top_list_count?type={}&interval_id=100%3A90'.format(typ)
ua = UserAgent()
html = requests.get(url=url, headers={'User-Agent': ua.random}).json()
total = html['total']
return total
def main(self):
typ = input('請(qǐng)輸入電影類(lèi)型(劇情|喜劇|動(dòng)作):')
typ_dict = {'劇情': '11', '喜劇': '24', '動(dòng)作': '5'}
typ = typ_dict[typ]
total = self.get_total(typ) # 獲取該類(lèi)型電影總數(shù)量
for page in range(0, int(total), 20):
params = {
'type': typ,
'interval_id': '100:90',
'action': '',
'start': str(page),
'limit': '20'}
self.get_html(params)
time.sleep(1)
print('爬取的電影的數(shù)量:', self.i)
if __name__ == '__main__':
spider = DoubanSpider()
spider.main()
騰訊招聘數(shù)據(jù)抓取(Ajax)
確定URL地址及目標(biāo)
URL: 百度搜索騰訊招聘 - 查看工作崗位 https://careers.tencent.com/search.html
目標(biāo): 職位名稱(chēng)、工作職責(zé)、崗位要求
要求與分析
通過(guò)查看網(wǎng)頁(yè)源碼,得知所需數(shù)據(jù)均為 Ajax 動(dòng)態(tài)加載
通過(guò)F12抓取網(wǎng)絡(luò)數(shù)據(jù)包,進(jìn)行分析
一級(jí)頁(yè)面抓取數(shù)據(jù): 職位名稱(chēng)
二級(jí)頁(yè)面抓取數(shù)據(jù): 工作職責(zé)、崗位要求
一級(jí)頁(yè)面json地址(pageIndex在變,timestamp未檢查)
https://careers.tencent.com/tencentcareer/api/post/Query?timestamp=1563912271089&countryId=&cityId=&bgIds=&productId=&categoryId=&parentCategoryId=&attrId=&keyword=&pageIndex={}&pageSize=10&language=zh-cn&area=cn
二級(jí)頁(yè)面地址(postId在變,在一級(jí)頁(yè)面中可拿到)
https://careers.tencent.com/tencentcareer/api/post/ByPostId?timestamp=1563912374645&postId={}&language=zh-cn
useragents.py文件
ua_list = [ 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/14.0.835.163 Safari/535.1', 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:6.0) Gecko/20100101 Firefox/6.0', 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; InfoPath.3)', ]
import time
import json
import random
import requests
from useragents import ua_list
class TencentSpider(object):
def __init__(self):
self.one_url = 'https://careers.tencent.com/tencentcareer/api/post/Query?timestamp=1563912271089&countryId=&cityId=&bgIds=&productId=&categoryId=&parentCategoryId=&attrId=&keyword=&pageIndex={}&pageSize=10&language=zh-cn&area=cn'
self.two_url = 'https://careers.tencent.com/tencentcareer/api/post/ByPostId?timestamp=1563912374645&postId={}&language=zh-cn'
self.f = open('tencent.json', 'a') # 打開(kāi)文件
self.item_list = [] # 存放抓取的item字典數(shù)據(jù)
# 獲取響應(yīng)內(nèi)容函數(shù)
def get_page(self, url):
headers = {'User-Agent': random.choice(ua_list)}
html = requests.get(url=url, headers=headers).text
html = json.loads(html) # json格式字符串轉(zhuǎn)為Python數(shù)據(jù)類(lèi)型
return html
# 主線(xiàn)函數(shù): 獲取所有數(shù)據(jù)
def parse_page(self, one_url):
html = self.get_page(one_url)
item = {}
for job in html['Data']['Posts']:
item['name'] = job['RecruitPostName'] # 名稱(chēng)
post_id = job['PostId'] # postId,拿postid為了拼接二級(jí)頁(yè)面地址
# 拼接二級(jí)地址,獲取職責(zé)和要求
two_url = self.two_url.format(post_id)
item['duty'], item['require'] = self.parse_two_page(two_url)
print(item)
self.item_list.append(item) # 添加到大列表中
# 解析二級(jí)頁(yè)面函數(shù)
def parse_two_page(self, two_url):
html = self.get_page(two_url)
duty = html['Data']['Responsibility'] # 工作責(zé)任
duty = duty.replace('\r\n', '').replace('\n', '') # 去掉換行
require = html['Data']['Requirement'] # 工作要求
require = require.replace('\r\n', '').replace('\n', '') # 去掉換行
return duty, require
# 獲取總頁(yè)數(shù)
def get_numbers(self):
url = self.one_url.format(1)
html = self.get_page(url)
numbers = int(html['Data']['Count']) // 10 + 1 # 每頁(yè)有10個(gè)推薦
return numbers
def main(self):
number = self.get_numbers()
for page in range(1, 3):
one_url = self.one_url.format(page)
self.parse_page(one_url)
# 保存到本地json文件:json.dump
json.dump(self.item_list, self.f, ensure_ascii=False)
self.f.close()
if __name__ == '__main__':
start = time.time()
spider = TencentSpider()
spider.main()
end = time.time()
print('執(zhí)行時(shí)間:%.2f' % (end - start))
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
OpenCV清除小面積連通域的實(shí)現(xiàn)方法
本文主要介紹了OpenCV清除小面積連通域的實(shí)現(xiàn)方法,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-09-09
使用PyTorch常見(jiàn)4個(gè)錯(cuò)誤解決示例詳解
這篇文章主要為大家介紹了使用PyTorch常見(jiàn)4個(gè)錯(cuò)誤解決示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-10-10
Python semaphore evevt生產(chǎn)者消費(fèi)者模型原理解析
這篇文章主要介紹了Python semaphore evevt生產(chǎn)者消費(fèi)者模型原理解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-03-03
python打包exe文件并隱藏執(zhí)行CMD命令窗口問(wèn)題
這篇文章主要介紹了python打包exe文件并隱藏執(zhí)行CMD命令窗口問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-01-01
Python如何使用pymongo連接MongoDB數(shù)據(jù)庫(kù)并進(jìn)行相關(guān)操作
PyMongo是驅(qū)動(dòng)程序,使python程序能夠使用Mongodb數(shù)據(jù)庫(kù),使用python編寫(xiě)而成,下面這篇文章主要給大家介紹了關(guān)于Python如何使用pymongo連接MongoDB數(shù)據(jù)庫(kù)并進(jìn)行相關(guān)操作的相關(guān)資料,需要的朋友可以參考下2023-05-05

