Talk is cheap,Show me your code.
官方文檔:http://html.python-requests.org
中文文檔:https://cncert.github.io/requests-html-doc-cn/#/?id=rebuild_methodprepared_request-response
GitHub:https://github.com/Liangchengdeye/Requests_Html_Spider
一、簡(jiǎn)介
1坑填、編寫爬蟲時(shí)requests+BeautifulSoup是一對(duì)完美的組合你踩,先通過requests模塊將網(wǎng)頁爬取下來伍俘,再交給BeautifulSoup等一些html解析庫進(jìn)行解析叽掘,而requests_html可以直接解析。
2道偷、優(yōu)點(diǎn)
- 支持JavaScript
- 支持CSS選擇器
- 支持xpath選擇器
- 模擬用戶代理
- 自動(dòng)重定向
- 連接池和cookie持久性
- 支持異步
Tips:看文檔說這個(gè)模塊只支持py3.6缀旁,但這個(gè)庫還在更新,所以py3.6及以上版本應(yīng)該都能用勺鸦。
3并巍、安裝
pip install requests-html
二、使用
1换途、請(qǐng)求網(wǎng)頁
from requests_html import HTMLSession
session = HTMLSession()
參數(shù):
browser.args = [
'--no-sand',
'--user-agent = XXXXX'
]
比如:
session = HTMLSession(
browser_args=[
'--no-sand',
'--user-agent=計(jì)算機(jī)配置'
] # , headless=False # ??去改源碼
)
get請(qǐng)求參數(shù):
url 【請(qǐng)求的路由】
headers = {} 【頭部信息】 優(yōu)先級(jí)高于cookie
params = {} 【參數(shù)】
proxies = {'http':'http://端口:ip'} 【代理IP】
timeout = 0.5 【網(wǎng)頁響應(yīng)超時(shí)時(shí)間】
allow_redirects = False 【是否允許重定向懊渡,默認(rèn)True】
post請(qǐng)求參數(shù):
url 【請(qǐng)求的路由】
header = {} 【頭部信息】
cookies = {} 【header中有cookie信息,另外單獨(dú)傳入cookie】
data = {} 【post請(qǐng)求數(shù)據(jù)是放在數(shù)據(jù)體中的】
json = {} 【json格式的數(shù)據(jù)】
files = {'file':open(...,'rb')} 【文件數(shù)據(jù)】
timeout = 0.5 【網(wǎng)頁響應(yīng)超時(shí)時(shí)間】
allow_redirects = False 【是否允許重定向军拟,默認(rèn)True】
??源碼改動(dòng)3處
(1)from requests_html import HTMLSession 進(jìn)入
(2)class HTMLSession(BaseSession):
def __init__(self, **kwargs):
super(HTMLSession, self).__init__(**kwargs)
(3)因?yàn)槔^承的是BaseSession剃执,所以進(jìn)入BaseSession修改三處地方
2、響應(yīng)
url = "要請(qǐng)求的網(wǎng)頁鏈接"
響應(yīng)對(duì)象 = session.get(url=url)
響應(yīng)對(duì)象 = session.post(url=url)
響應(yīng)對(duì)象 = session.request(method='get/post',url=url)
響應(yīng)屬性:
響應(yīng)對(duì)象.url 【獲取url】
響應(yīng)對(duì)象.text 【獲取響應(yīng)文本】
響應(yīng)對(duì)象.encoding = 'GBK/UTF_8' 【設(shè)置網(wǎng)頁編碼】
響應(yīng)對(duì)象.content 【獲取網(wǎng)頁上的二進(jìn)制圖片懈息、視頻】
響應(yīng)對(duì)象.json() 【獲取json格式數(shù)據(jù)】
響應(yīng)對(duì)象.status_code 【狀態(tài)碼】
響應(yīng)對(duì)象.headers 【獲取頭部信息】
響應(yīng)對(duì)象.cookies 【獲取cookies信息】
響應(yīng)對(duì)象.history 【獲取歷史信息】
3肾档、解析
這個(gè)庫是解析HTML,所以解析的都是html對(duì)象
(1) html對(duì)象屬性(值):
響應(yīng)對(duì)象.html.absolute_links 【絕對(duì)路徑辫继,自動(dòng)添加http://怒见,自動(dòng)補(bǔ)全和其他鏈接地址一樣,返回的鏈接自動(dòng)轉(zhuǎn)為絕對(duì)路徑】
響應(yīng)對(duì)象.html.links 【以列表形式提取姑宽、返回響應(yīng)源碼中的所有url鏈接】
響應(yīng)對(duì)象.html.base_url 【基礎(chǔ)路徑遣耍,就是主路徑,一般是一個(gè)網(wǎng)站的網(wǎng)址】
響應(yīng)對(duì)象.html.html 【html網(wǎng)頁】
響應(yīng)對(duì)象.html.text 【網(wǎng)頁中的文本內(nèi)容】
響應(yīng)對(duì)象.html.encoding 【獲取編碼方式】
響應(yīng)對(duì)象.html.encoding='GBK' 【輸出的文本出現(xiàn)亂碼低千,這時(shí)就需要指定編碼方式配阵,??在控制臺(tái)輸入document.charset查看編碼類型】
響應(yīng)對(duì)象.html.raw_html 【未解析過的網(wǎng)頁】
響應(yīng)對(duì)象.html.pq 【<class 'pyquery.pyquery.PyQuery'> pyquery對(duì)象】
????(2) html對(duì)象方法:
- 響應(yīng)對(duì)象.html.find () —— 使用css解析器
r.html.find('css選擇器')
r.html.find('css選擇器',first=True)
r.html.find('#footer') # 查找footer標(biāo)簽元素對(duì)象
r.html.find('a',first=True) # 查找第一個(gè)a標(biāo)簽元素對(duì)象
???find函數(shù)有5個(gè)參數(shù):
?selector【css解析式,要用的CSS選擇器】
?first 【布爾值示血,如果為真則表示獲取符合css表達(dá)式的第一個(gè)元素對(duì)象棋傍,否則返回滿足條件的元素對(duì)象列表】
?containing【如果指定,則只返回包含提供文本的元素难审,放在列表中】
?clean【布爾值瘫拣,是否消除 <script> 和<style> 標(biāo)簽對(duì)html產(chǎn)生的影響】
?_encoding【編碼方式】
???因?yàn)楹瘮?shù)獲得的是元素,所以元素還有他的屬性
----- 響應(yīng)對(duì)象.html.find().text ----
r.html.find('a', first=True).text >>> 獲取元素的文本內(nèi)容
>>>輸出:懈婧埃花網(wǎng)
----- 響應(yīng)對(duì)象.html.find().attrs ----
r.html.find('a', first=True) >>> 查找元素對(duì)象
r.html.find('a', first=True).attrs) >>>獲取元素所有的屬性
r.html.find('a', first=True).attrs['href'] >>> 獲取元素的具體屬性麸拄,返回值是個(gè)字典
>>>輸出:
>>> <Element 'a' title='校花網(wǎng)'>
>>> {'href': 'http://www.xiaohuar.com', 'title': '星花網(wǎng)'}
>>> http://www.xiaohuar.com
----- 響應(yīng)對(duì)象.html.find().html ----
r.html.find('a', first=True).html >>> 查找html內(nèi)容
>>>輸出 <a title="校花网">新G校花網(wǎng)</a>
??CSS選擇器:
??(1) 類名選擇器
????class ——> .類名
??(2) id選擇器
????id ——> #id名
??(3) 標(biāo)簽選擇器
????p ——> 標(biāo)簽p
??(4) 后代選擇器
????所有:兒子,孫子秆吵,重孫...
????ul li a
??(5) 子選擇器
????只有兒子
????ul li
??(6) 屬性選擇器(5種)
????[屬性] 用于選取帶有指定屬性的元素
????[屬性=值] 用于選取帶有指定屬性和值的元素
????[屬性^=值] 匹配屬性值以指定值開頭的每個(gè)元素
????[屬性$=值] 匹配屬性值以指定值結(jié)尾的每個(gè)元素
????[屬性~=值] 用于選取屬性值中包含指定詞匯的元素
????[屬性=*值] 匹配屬性值中包含指定值的每個(gè)元素
??(7) 群組選擇器
????選擇器1淮椰,選擇器2... 相當(dāng)于or
??(8) 多條選擇器
????選擇器1選擇器2... 相當(dāng)于and
更多用法:https://www.w3school.com.cn/cssref/css_selectors.asp
- 響應(yīng)對(duì)象.html.xpath() —— 使用path解析器
r.html.xpath('xpath選擇器')
r.html.xpath('xpath選擇器',first=True)
h.html.xpath("http://div[@id='menu']/a")
h.html.xpath("http://div[@id='menu']",first=True
參數(shù)和屬性參照css選擇器。
??XPath選擇器
語法:
(1) 選取節(jié)點(diǎn)
表達(dá)式 | 描述 |
---|---|
nodename | 選取此節(jié)點(diǎn)的所有子節(jié)點(diǎn) |
/ | 從根節(jié)點(diǎn)選取 |
// | 從匹配選擇的當(dāng)前節(jié)點(diǎn)選擇文檔中的節(jié)點(diǎn)纳寂,而不考慮它們的位置 |
. | 選取當(dāng)前節(jié)點(diǎn) |
.. | 選取當(dāng)前節(jié)點(diǎn)的父節(jié)點(diǎn) |
@ | 選取屬性 |
實(shí)例??
路徑表達(dá)式 | 結(jié)果 |
---|---|
bookstore | 選取 bookstore 元素的所有子節(jié)點(diǎn) |
/bookstore | 選取根元素 bookstore主穗。 注釋:假如路徑起始于正斜杠( / ),則此路徑始終代表到某元素的絕對(duì)路徑毙芜! |
bookstore/book | 選取屬于 bookstore 的子元素的所有 book 元素 |
//book | 選取所有 book 子元素忽媒,而不管它們?cè)谖臋n中的位置 |
bookstore//book | 選擇屬于 bookstore 元素的后代的所有 book 元素,而不管它們位于 bookstore 之下的什么位置 |
//@lang | 選取名為 lang 的所有屬性 |
(2)謂語:用來查找某個(gè)特定的節(jié)點(diǎn)或者包含某個(gè)指定的值的節(jié)點(diǎn)
實(shí)例??
路徑表達(dá)式 | 結(jié)果 |
---|---|
/bookstore/book[1] | 選取屬于 bookstore 子元素的第一個(gè) book 元素 |
/bookstore/book[last()] | 選取屬于 bookstore 子元素的最后一個(gè) book 元素 |
/bookstore/book[last()-1] | 選取屬于 bookstore 子元素的倒數(shù)第二個(gè) book 元素 |
/bookstore/book[position()<3] | 選取最前面的兩個(gè)屬于 bookstore 元素的子元素的 book 元素 |
//title[@lang] | 選取所有擁有名為 lang 的屬性的 title 元素 |
//title[@lang='eng'] | 選取所有 title 元素腋粥,且這些元素?fù)碛兄禐?eng 的 lang 屬性 |
/bookstore/book[price>35.00] | 選取 bookstore 元素的所有 book 元素晦雨,且其中的 price 元素的值須大于 35.00 |
/bookstore/book[price>35.00]/title | 選取 bookstore 元素中的 book 元素的所有 title 元素,且其中的 price 元素的值須大于 35.00 |
(3) 選取未知節(jié)點(diǎn)
通配符 | 描述 |
---|---|
* | 匹配任何元素節(jié)點(diǎn) |
@* | 匹配任何屬性節(jié)點(diǎn) |
node() | 匹配任何類型的節(jié)點(diǎn) |
實(shí)例??
路徑表達(dá)式 | 結(jié)果 |
---|---|
/bookstore/* | 選取 bookstore 元素的所有子元素 |
//* | 選取文檔中的所有元素 |
//title[@*] | 選取所有帶有屬性的 title 元素 |
(4) 選取若干路徑
實(shí)例??:通過在路徑表達(dá)式中使用“|”運(yùn)算符灯抛,可以選取若干個(gè)路徑
路徑表達(dá)式 | 結(jié)果 |
---|---|
//book/title 豎杠 //book/price | 選取 book 元素的所有 title 和 price 元素 |
//title 豎杠 //price | 選取文檔中的所有 title 和 price 元素 |
/bookstore/book/title 豎杠 //price | 選取屬于 bookstore 元素的 book 元素的所有 title 元素金赦,以及文檔中所有的 price 元素 |
更多用法:https://www.w3school.com.cn/xpath/index.asp
- 響應(yīng)對(duì)象.html.search() —— 搜索元素的文本內(nèi)容
r.html.search('模板') # 查出一個(gè)
r.html.search_all('模板') # 查出所有
??:r.html.search('我覺得很有{}理')[0] >>>輸出:道
r.html.search('我覺得很{a}道{test}')['test'] >>>輸出:理
??tips:如果沒有找到的話返回網(wǎng)頁
- 響應(yīng)對(duì)象.html.render() —— JavaScript支持
內(nèi)置支持js渲染,對(duì)以往需要使用Selenium獲取源碼的頁面可不再額外使用Selenium庫操作对嚼。
render方法將會(huì)渲染頁面的JavaScript夹抗,返回渲染后的數(shù)據(jù)。
特別注意的是:在初次使用該功能的時(shí)候會(huì)自動(dòng)下載支持包:Chromium
from requests_html import HTMLSession
session = HTMLSession()
r = session.get('http://python-requests.org/')
r.html.render()
??render()方法可用參數(shù):
??retries 【在Chromium里加載頁面的重試次數(shù)】
??script 【執(zhí)行頁面上的JavaScript (可選參數(shù)) 】
??wait 【頁面加載前的等待時(shí)間纵竖,防止超時(shí) (單位:秒漠烧,可選參數(shù)) 】
??scrolldown 【接收整數(shù)參數(shù)n,如果提供參數(shù)n靡砌,表示向后翻n頁】
??sleep 【在頁面初次渲染之后的等待時(shí)間】
??reload 【如果為False已脓,則不會(huì)重新從瀏覽器加載內(nèi)容,而是讀取內(nèi)存里的內(nèi)容】
??keep_page 【如果為True通殃,將會(huì)允許你通過r.html.page與瀏覽器頁面交互】
??Tips:如果scrolldown和sleep都指定度液,那么程序會(huì)在暫停相應(yīng)時(shí)間后厕宗,再往后翻頁面(如:scrolldown=10, sleep=1)
scripy:"""( ) => {
? js代碼
? js代碼
? }
? """
? scrolldow:n
? sleep:n
? keep_page:True/False
()=>{
Object.defineProperties(navigator,{
webdriver:{
get: () => undefined
}
})
??與瀏覽器交互
(1) 頁面事件 —— r.html.page.XXX
----- 格式 -----
async def 函數(shù)名(): # 先定義一個(gè)函數(shù)
await r.html.page.XXX # 交互方式
session.loop.run_until_complete(函數(shù)名()) # 調(diào)用
----- 交互方式 -----
r.html.page.screenshot({"path":路徑}) # 截屏
r.html.page.evaluate("""()=>{js代碼}""")
r.html.page.cookies()
r.html.page.type('css選擇器','內(nèi)容',{'delay':100})
r.html.page.click('css選擇器') # 點(diǎn)擊
r.html.page.focus('css選擇器') # 聚焦
r.html.page.hover('css選擇器') # 懸浮
r.html.page.waitForSelector('css選擇器')
r.html.page.waitFor(1000) # 等待
(2) 鍵盤事件 —— r.html.page.keyboard.XXX
r.html.page.keyboard.type('咸魚今天努力了沒',{'delay':100}) # 鍵盤打字
r.html.page.keyboard.up('Shift') # Shift抬起,按鍵可以換成別的
r.html.page.keyboard.down('Shift') # Shift按下
r.html.page.keyboard.press('ArrowLeft') # 一直按左鍵進(jìn)行選擇
(3) 鼠標(biāo)事件 —— r.html.page.mouse.XXX
r.html.page.mouse.click(x,y,{ # 點(diǎn)擊
'button':'left',
'click':1
'delay':0
})
r.html.page.mouse.down({'button':'left'}) # 鼠標(biāo)按下
r.html.page.mouse.up({'button':'left'}) # 鼠標(biāo)抬起
??一般up和down一起使用堕担,表示點(diǎn)擊一下
r.html.page.mouse.movie(x,y,{'steps':1}) # 鼠標(biāo)移動(dòng)已慢,步長為1表示移動(dòng)一步就到,步長設(shè)置的越大霹购,則步數(shù)越細(xì)佑惠,移動(dòng)越慢
練習(xí):
from requests_html import HTMLSession
session = HTMLSession(
browser_args=[
'--no-sand',
'--user-agent=自己的電腦配置'
] # , headless=False # ??去改源碼
)
url = 'http://www.google.com'
# r = session.request(method='get', url=url)
# print(dir(r.html.find('a',first=True)))
#
# a_element = r.html.find('#footer',first=True)
# print(a_element.attrs['name'])
# print(r.html.search_all('(提示:{name},最新章節(jié)可能會(huì){pwd}齐疙,登錄書架即可實(shí)時(shí)查看膜楷。)')[0])
r = session.request(method='get', url='https://www.bilibili.com/')
scrapts = """
()=>{
Object.defineProperties(navigator,{
webdriver:{
get:() => undefined
}
})}
"""
try:
r.html.render(script=scrapts, sleep=1, keep_page=True)
async def main():
await r.html.page.screenshot({"path": '1.png', 'clip': {'x': 200, 'y': 200, 'width': 400, 'height': 400}})
res = await r.html.page.evaluate("""
()=>{
var a = document.querySelector("#list")
return {'x':a.offsetLeft}
}
""")
print(res)
print(await r.html.page.cookies())
await r.html.page.type('#kw', '瀧澤蘿拉', {'delay': 500})
await r.html.page.waitForSelector('[name="tj_trnews"]')
await r.html.page.click('[name="tj_trnews"]')
await r.html.page.focus('[type="number"]')
await r.html.page.keyboard.type('111', {'delay': 200})
await r.html.page.hover('[data-stat-id="6f5c93b4d1baf5e9"]')
await r.html.page.keyboard.type('喜歡你啊', {'delay': 200})
await r.html.page.keyboard.down('Shift')
for i in range(3):
await r.html.page.keyboard.press('ArrowLeft', {'delay': 1000})
await r.html.page.keyboard.up('Shift')
await r.html.page.keyboard.up('Backspace')
res = await r.html.page.evaluate('''
()=>{
car a = document.querySelector('[alt="【究極爆肝】德克薩斯與拉普蘭德的感傷往事(明日方舟描改 動(dòng)畫手書·完整版)"]')
return {
'x':a.x+a.width/2,
'y':a.y+a.height/2
}
}
''')
print(res)
await r.html.page.mouse.movie(res['x'], res['y'], {'step': 200})
await r.html.page.mouse.down({'button': 'right'})
await r.html.page.mouse.up({'button': 'right'})
await r.html.page.mouse.click(res['x'], res['y'])
await r.html.page.waitFor(5000)
session.loop.run_until_complete(main())
finally:
session.close()
實(shí)戰(zhàn):
1、姓攴埽花網(wǎng)圖片
# 對(duì)頁碼進(jìn)行分析
# http://www.xiaohuar.com/hua/
# http://www.xiaohuar.com/list-1-1.html 第2頁
# http://www.xiaohuar.com/list-1-2.html 第3頁
import os
from requests_html import HTMLSession
# 生成所有頁碼url
def get_page_url():
for i in range(46):
yield 'http://www.xiaohuar.com/list-1-{}.html'.format(i)
session = HTMLSession()
# 第一頁解析測(cè)試
url = "http://www.xiaohuar.com/list-1-0.html"
# 解析頁面赌厅,獲取圖片名和url
def parse_page(url):
r = session.request(method='get', url=url)
img_element_list = r.html.find('[class="img"] img')
for img_element in img_element_list:
file_name1 = img_element.attrs.get('alt').replace('/', '').replace('\\', '')
# print(file_name)
file_name = file_name1 + '.png'
file_url: str = img_element.attrs.get('src')
file_url = r.html.base_url[:-1] + file_url if not file_url.startswith("http") else file_url
save_file(file_name, file_url)
# 保存圖片
def save_file(name, url):
base_path = '校花圖片'
file_path = os.path.join(base_path, name)
r = session.get(url=url)
with open(file_path, 'wb') as f:
f.write(r.content)
print('%s下載成功' % name)
if __name__ == '__main__':
for page_url in get_page_url():
parse_page(page_url)
2轿塔、胁於祝花網(wǎng)視頻
"""
分析:校花網(wǎng)視頻分為兩種催训,一種是mp4格式的洽议,一種是m3u8格式的,所以要分別進(jìn)行處理
"""
from requests_html import HTMLSession
import os
session = HTMLSession()
# 獲取索引頁url
def get_index_page():
for i in range(6):
url = 'http://www.xiaohuar.com/list-3-%s.html' % i
yield url
# 解析索引頁獲取詳情頁url
# 解析詳情頁
def get_detail_page(url):
r = session.get(url=url)
for element in r.html.find('#images a[class="imglink"]'):
yield element.attrs.get('href')
# 解析詳情頁獲取視頻url漫拭,名字
# 獲得名字亚兄,類型,鏈接
def get_url_name(url):
r = session.get(url=url)
r.html.encoding = 'gbk'
file_name = r.html.find("title", first=True).text.replace('\\', '')
print(file_name)
element = r.html.find('#media source', first=True)
if element:
vurl = element.attrs.get('src')
vtype = 'mp4'
else:
vurl = r.html.search('var vHLSurl = "{}";')[0]
vtype = 'm3u8'
return file_name, vurl, vtype
# 保存文件
def save(file_name, vurl, vtype):
if vtype == "mp4":
file_name += ".mp4"
r = session.get(url=vurl)
with open(file_name, 'wb') as f:
f.write(r.content)
elif vtype == "m3u8":
save_m3u8(file_name, vurl)
# 處理m3u8
# save_m3u8('xxx','https://www6.laqddcc.com/hls/2019/05/05/BRsIeDpx/playlist.m3u8')
def save_m3u8(file_name, vurl):
if not os.path.exists(file_name):
os.mkdir(file_name)
r = session.get(url=vurl)
m3u8_path = os.path.join(file_name, 'playlist.m3u8')
with open(m3u8_path, 'wb') as f:
f.write(r.content)
for line in r.text:
if line.endswith('ts'):
ts_url = vurl.replace('playlist.m3u8', line)
ts_path = os.path.join(file_name, line)
r0 = session.get(url=ts_url)
with open(ts_path, 'wb') as f:
f.write(r0.content)
if __name__ == '__main__':
for index_page in get_index_page():
for detail_url in get_detail_page(index_page):
file_name, vurl, vtype = get_url_name(detail_url)
save(file_name, vurl, vtype)
3采驻、模擬知乎登錄
"""
分析:知乎用密碼登錄审胚,總共發(fā)送3次請(qǐng)求,請(qǐng)求路由相同礼旅,但請(qǐng)求方式不同膳叨;驗(yàn)證碼有兩種方式,字母驗(yàn)證碼和選擇倒立文字驗(yàn)證碼痘系;兩個(gè)加密菲嘴,formdata加密和signature加密。
3次請(qǐng)求:【第一次】https://www.zhihu.com/api/v3/oauth/captcha?lang=en get請(qǐng)求檢測(cè)是否需要驗(yàn)證碼,傳輸?shù)氖莏son格式汰翠;
【第二次】https://www.zhihu.com/api/v3/oauth/captcha?lang=en put獲取驗(yàn)證碼龄坪,返回的是base64格式的驗(yàn)證碼;
【第三次】https://www.zhihu.com/api/v3/oauth/captcha?lang=en post請(qǐng)求發(fā)送驗(yàn)證碼复唤。
兩種驗(yàn)證碼:lang=en 英文健田;lang=cn 中文。
signature加密是使用sha1和hmac佛纫;
formdata加密是通過導(dǎo)入加密的js函數(shù)來反解出的妓局。
"""
import requests
import base64
import Image
from time import sleep
import hmac
from hashlib import sha1
import time
from urllib.parse import urlencode
import execjs
import json
class Zhihu(object):
def __init__(self):
self.session = requests.session()
self.headers = {
'referer': 'https://www.zhihu.com/signin?next=%2F',
'user-agent': '自己的電腦配置'
}
self.picture = None # 存驗(yàn)證碼
self.signature = None # 存簽名
self.picture_url = None # 存驗(yàn)證碼鏈接
def getbasecookie(self):
self.session.get(url='https://www.zhihu.com/signin?next=%2F', headers=self.headers)
self.session.get(url='https://www.zhihu.com/api/v4/search/preset_words', headers=self.headers)
# self.session.post(url='https://www.zhihu.com/udid', headers=self.headers) # 未知
def getcapture(self):
# 獲取驗(yàn)證碼方法总放,有時(shí)候不用獲取驗(yàn)證碼就可以直接登錄
# lang=en是英文字母,lang=cn是選倒過來的文字
message = self.session.get(url='https://www.zhihu.com/api/v3/oauth/captcha?lang=en',
headers=self.headers).json() # get請(qǐng)求檢測(cè)是否需要驗(yàn)證碼,傳輸?shù)氖莏son格式
print(message) # {'show_captcha':True/False}
if message['show_captcha'] == False: # 如果不需要驗(yàn)證碼
self.picture = ''
else:
self.picture_url = self.session.put(url='https://www.zhihu.com/api/v3/oauth/captcha?lang=en',
headers=self.headers).json() # put獲取驗(yàn)證碼好爬,返回的是base64格式的驗(yàn)證碼
# 采用base64格式將驗(yàn)證碼通過圖片格式顯示出來
with open('captcha.jpg', 'wb') as f:
f.write(base64.b64decode(self.picture_url['img_base64']))
image = Image.open('captcha.jpg')
image.show()
self.picture = input('請(qǐng)輸入驗(yàn)證碼:')
sleep(2)
message1 = self.session.post(url='https://www.zhihu.com/api/v3/oauth/captcha?lang=en',data={'input_text':self.picture},headers=self.headers).json() # post請(qǐng)求發(fā)送驗(yàn)證碼
print(message1)
def get_signature(self):
# 知乎登錄的主要問題在于找到signature间聊,重點(diǎn)
a = hmac.new('d1b964811afb40118a12068ff74a12f4'.encode('utf-8'),digestmod=sha1)
a.update('password'.encode('utf-8'))
a.update(b'c3cef7c66a1843f8b3a9e6a1e3160e20')
a.update(b'com.zhihu.web')
a.update(str(int(time.time()*1000)).encode('utf-8'))
self.signature = a.hexdigest()
def Login_phone(self):
# 登錄
data = {
'client_id': 'c3cef7c66a1843f8b3a9e6a1e3160e20',
'grant_type': 'password',
'timestamp': str(int(time.time() * 1000)),
'source': 'com.zhihu.web',
'signature': self.signature,
'username': '+8615151979063',
'password': '88404620wpr',
'captcha': self.picture,
'lang': 'en',
'utm_source': '',
'ref_source': 'other_https://www.zhihu.com/signin?next=%2F',
}
headers = {
'scheme': 'https',
'accept': '*/*',
'accept-encoding': 'gzip, deflate, br',
'accept-language': 'zh-CN,zh;q=0.8',
'cache-control': 'no-cache',
# 'content-length':'412',
'pragma': 'no-cache',
'origin': 'https://www.zhihu.com',
'user-agent':'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36 SE 2.X MetaSr 1.0',
'content-type': 'application/x-www-form-urlencoded',
'referer': 'https://www.zhihu.com/signin?next=%2F',
'x-xsrftoken': self.session.cookies.get('_xsrf'),
'x-requested-with': 'fetch',
'x-ab-param': 'top_vipconsume=1;se_famous=1;se_featured=1;top_ebook=0;top_recall_exp_v1=1;se_topicdirect=2;li_ts_sample=old;qa_answerlist_ad=0;zr_ans_rec=gbrank;se_likebutton=0;se_d2q=0;top_root=0;pf_noti_entry_num=0;li_android_vip=0;se_limit=0;se_ltr_nn=0;se_time_threshold=0;top_gr_ab=0;pf_fuceng=1;ls_videoad=0;top_ydyq=X;qa_test=0;zr_art_rec=base;se_topiclabel=1;top_recall_exp_v2=1;ls_new_upload=0;zr_km_answer=open_cvr;se_webtimebox=0;se_backsearch=0;tp_meta_card=0;zr_search_xgb=0;zr_km_xgb_model=new_xgb;se_spb309=0;soc_special=0;li_album3_ab=0;li_se_xgb=0;se_p_slideshow=0;ls_fmp4=0;soc_bignew=1;soc_bigone=0;ug_zero_follow_0=0;zr_article_rec_rank=close;zr_km_paid_answer=0;se_ri=0;tp_sft_v2=d;zr_rec_answer_cp=close;se_agency= 0;tsp_hotctr=1;soc_notification=0;pf_foltopic_usernum=50;zr_video_rank=new_rank;se_whitelist=0;tp_sft=a;tp_header_style=1;ug_zero_follow=0;li_se_paid_answer=0;se_go_ztext=0;tp_topic_head=1;top_reason=1;li_qa_new_cover=0;se_expired_ob=0;top_recall_deep_user=1;tsp_lastread=0;li_qa_cover=old;li_price_test=1;tp_qa_metacard_top=top;se_rr=0;se_wannasearch=0;se_subtext=0;pf_creator_card=1;zr_km_tag=open;se_mclick1=0;se_movietab=1;top_v_album=1;ug_newtag=0;zr_km_slot_style=event_card;se_page_limit_20=1;se_zu_recommend=0;top_rank=0;zr_intervene=0;se_billboardsearch=0;se_new_topic=0;se_payconsult=0;soc_update=1;pf_feed=1;zr_answer_rec_cp=open;se_ltr_ck=0;tp_qa_metacard=1;top_universalebook=1;se_websearch=3;ug_goodcomment_0=1;ug_follow_answerer=0;zr_infinity_small=256;se_mclick=0;top_native_answer=1;li_hot_score_ab=0;zr_rel_search=base;zr_video_recall=current_recall;se_ad_index=10;se_college_cm=0;ug_fw_answ_aut_1=0;zr_video_rank_nn=new_rank;se_college=default;se_search_feed=N;ug_follow_topic_1=2;li_back=0;se_webrs=1;se_amovietab=1;top_new_feed=5;li_pay_banner_type=0;li_ebook_detail=1;se_preset_tech=0;se_colorfultab=1;tp_sticky_android=0;li_tjys_ec_ab=0;ug_follow_answerer_0=0;li_album_liutongab=0;li_se_kv=0;se_mobileweb=1;top_quality=0;se_lottery=0;top_test_4_liguangyi=1;zr_km_style=base;se_ios_spb309=0;se_auto_syn=0;tsp_vote=1;pf_newguide_vertical=0;tsp_childbillboard=1;li_se_album_card=0;se_ltr_gc=0;tp_qa_toast=1;tp_m_intro_re_topic=1;se_waterfall=0;top_hotcommerce=1;zr_infinity_a_u=close;se_webmajorob=0;se_zu_onebox=0;se_site_onebox=0',
'x-zse-83': '3_2.0',
}
print(self.session.cookies.get('_xsrf'))
print(urlencode(data))
with open('知乎加密js.js','r',encoding='utf-8') as f:
js = execjs.compile(f.read()) # 傳入unicode字符
data = js.call(u'b',urlencode(data)) # data_dict為表單數(shù)據(jù)
print(data)
message = self.session.post(url='https://www.zhihu.com/api/v3/oauth/sign_in',headers=headers,data=data)
message.encoding='utf-8'
print(message.text)
print(json.loads(message.text)['error']['message'])
def target_url(self,url):
text = self.session.get(url)
return text.text
if __name__ == '__main__':
zhihu = Zhihu()
zhihu.getbasecookie() # 首次cookie
zhihu.getcapture() # 驗(yàn)證碼
zhihu.get_signature() # signature
zhihu.Login_phone() # 登錄