對知乎內容使用爬蟲爬取數據,為什麼會遇到403問題?

我想抓取知乎上用戶的關注信息,如查看A關注了哪些人,通過www.zhihu.com/people/XXX/followees這個頁面來獲得followee的列表,但是在抓取中遇到了403問題。
1.爬蟲僅僅是為了搜集用戶關注信息,用於學術研究,絕非商業或其他目的
2.使用PHP,利用curl構造請求,使用simple_html_dom來解析文檔
3.在用戶的關注者(Followees)列表,應該是使用Ajax進行動態載入更多的followees,於是我想直接爬介面的數據,通過firebug查看到,載入更多的關注者似乎是通過http://www.zhihu.com/node/ProfileFolloweesListV2 進行的,並且post的數據有_xsrf,method,parmas,於是我在模擬保持登錄的情況下,對這個鏈接提交請求,並帶有post過去的所需要的參數,但是返回的是403。
4.但是我同樣模擬登錄的情況下,可以解析到如贊同數、感謝數這些不需要Ajax的數據
5.我使用curl_setopt($ch, CURLOPT_HTTPHEADER, $header );來設置請求頭,使其與我在瀏覽器中提交的請求的請求頭一致,但是這樣任然導致403錯誤
6.我嘗試列印出curl的請求頭與瀏覽器發出的請求頭進行比較,但是沒有找到正確的方式(百度出的curl_getinfo()似乎列印出的相應報文)
7.有許多人曾因為沒有設置User-Agent或者X-Requested-With遭遇403,但是我在5中描述設置請求頭時都設置了
8.如果敘述不詳需要貼出代碼,我可以貼出代碼
9.這個爬蟲是我畢設的一部分,需要獲取數據來進行接下來的工作,如1所說,爬取數據純粹是為了學術研究


這兩天剛好做了一個抓取用戶的關注著和追隨者的的爬蟲在抓數據,使用的是Python。這裡給你一段python的代碼,你可以對著代碼看一下你的代碼問題。
403應該就是請求的時候一些數據發錯了,下面的代碼中涉及到一個打開的文本,文本中的內容是用戶的id,文本裡面的內容樣式我截了圖放在最後面。

#encoding=utf8
import urllib2
import json
import requests
from bs4 import BeautifulSoup

Default_Header = {"X-Requested-With": "XMLHttpRequest",
"Referer": "http://www.zhihu.com",
"User-Agent": "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; "
"rv:39.0) Gecko/20100101 Firefox/39.0",
"Host": "www.zhihu.com"}
_session = requests.session()
_session.headers.update(Default_Header)
resourceFile = open("/root/Desktop/UserId.text","r")
resourceLines = resourceFile.readlines()
resultFollowerFile = open("/root/Desktop/userIdFollowees.text","a+")
resultFolloweeFile = open("/root/Desktop/userIdFollowers.text","a+")

BASE_URL = "https://www.zhihu.com/"
CAPTURE_URL = BASE_URL+"captcha.gif?r=1466595391805type=login"
PHONE_LOGIN = BASE_URL + "login/phone_num"

def login():
"""登錄知乎"""
username = ""#用戶名
password = ""#密碼,注意我這裡用的是手機號登錄,用郵箱登錄需要改一下下面登錄地址
cap_content = urllib2.urlopen(CAPTURE_URL).read()
cap_file = open("/root/Desktop/cap.gif","wb")
cap_file.write(cap_content)
cap_file.close()
captcha = raw_input("capture:")
data = {"phone_num":username,"password":password,"captcha":captcha}
r = _session.post(PHONE_LOGIN, data)
print (r.json())["msg"]

def readFollowerNumbers(followerId,followType):
"""讀取每一位用戶的關注者和追隨者,根據type進行判斷"""
print followerId
personUrl = "https://www.zhihu.com/people/" + followerId.strip("
")
xsrf =getXsrf()
hash_id = getHashId(personUrl)
headers = dict(Default_Header)
headers["Referer"]= personUrl + "/follow"+followType
followerUrl = "https://www.zhihu.com/node/ProfileFollow"+followType+"ListV2"
params = {"offset":0,"order_by":"created","hash_id":hash_id}
params_encode = json.dumps(params)
data = {"method":"next","params":params_encode,"_xsrf":xsrf}

signIndex = 20
offset = 0
while signIndex == 20:
params["offset"] = offset
data["params"] = json.dumps(params)
followerUrlJSON = _session.post(followerUrl,data=data,headers = headers)
signIndex = len((followerUrlJSON.json())["msg"])
offset = offset + signIndex
followerHtml = (followerUrlJSON.json())["msg"]
for everHtml in followerHtml:
everHtmlSoup = BeautifulSoup(everHtml)
personId = everHtmlSoup.a["href"]
resultFollowerFile.write(personId+"
")
print personId

def getXsrf():
"""獲取用戶的xsrf這個是當前用戶的"""
soup = BeautifulSoup(_session.get(BASE_URL).content)
_xsrf = soup.find("input",attrs={"name":"_xsrf"})["value"]
return _xsrf

def getHashId(personUrl):
"""這個是需要抓取的用戶的hashid,不是當前登錄用戶的hashid"""
soup = BeautifulSoup(_session.get(personUrl).content)
hashIdText = soup.find("script", attrs={"data-name": "current_people"})
return json.loads(hashIdText.text)[3]

def main():
login()
followType = input("請配置抓取類別:0-抓取關注了誰 其它-被哪些人關注")
followType = "ees" if followType == 0 else "ers"
for followerId in resourceLines:
try:
readFollowerNumbers(followerId,followType)
resultFollowerFile.flush()
except:
pass

if __name__=="__main__":
main()


如果帶有防火牆功能的伺服器,連續抓取可能被幹掉,除非你有很多代理伺服器。或者最簡單用adsl不斷重新撥號更換ip


覺得可能會是 2 個原因造成的:

  1. 沒帶 cookies
  2. _xsrf 或 hash_id 錯誤

之前也做過一個一樣的小程序:
python爬蟲 模擬登陸知乎 推送知乎文章到kindle電子書 獲取自己的關注問題
同樣遇到這個403的問題。

後來自己解決了,因為在post數據的時候,根據抓包提交的數據,只有

print "page: %d" %offset
params={"offset":offset}
params_json=json.dumps(params)

data={
"method":"next",
"params":params_json,

}

headers_l={
"Host":"www.zhihu.com",
"Referer":"https://www.zhihu.com/question/following",
"User-Agent":agent,
"Origin":"https://www.zhihu.com",
"X-Requested-With":"XMLHttpRequest"
}
try:
s=session.post(url_next,data=data,headers=headers_l)

返回一直都是403,無論header怎麼做,把全部欄位都填充上去,也不行。

後來不斷嘗試中,把_xsrf 欄位填充上去。

data={
"method":"next",
"params":params_json,

"_xsrf":xsrf
}

然後就可以了。


無非就是那些, useragent,referer,token,cookie


剛剛好,我前不久剛學Python 爬的就是我關注的人的信息,寫成了博客,全過程都在這:
http://m.blog.csdn.net/article/details?id=52159871

我想你拿到403的原因是因為Post的data 裡面沒有加入_xsrf的緣故,這是用來防偽登陸的,雖然你模擬登陸知乎的時候傳入了這個數據,但是後面要獲取知乎數據時候還是要傳這個,但是我從瀏覽器裡面的Post 沒有看到要傳這個參數,是我自己摸索出來的,對了知乎很多數據都是使用了Ajax 動態載入的,這裡碰了些灰,也寫在博客里了


這個問題我來回答下吧,知乎在「_xsrf」這個欄位搞了個小動作,並不是首頁頁面取到的那個_xsrf 的值,而是在登錄成功後通過cookie返回的那個「_xsrf 」的值,所以你需要獲取正確的這個值,不然一直會報403錯誤(我是在Post提問時發現的,相信你遇到的問題類似,直接上代碼):

/// & /// 知乎提問
/// &
/// &

提問標題& /// &

詳細內容& /// &

登錄後獲取的cookie&public void ZhiHuFaTie(string question_title,string question_detail,CookieContainer cookie)
{
question_title=「提問內容」;
question_detail=「問題詳細描述」;

//遍歷cookie,獲取_xsrf 的值
var list = GetAllCookies(cookie);
foreach (var item in list)
{
if (item.Name == "_xsrf")
{
xsrf = item.Value;
break;
}
}
//發帖
var FaTiePostUrl = "http://www.zhihu.com/question/add";
var dd = topicStr.ToCharArray();
var FaTiePostStr = "question_title=" + HttpUtility.UrlEncode(question_title) + "question_detail=" + HttpUtility.UrlEncode(question_detail) + "anon=0topic_ids=" + topicId + "new_topics=_xsrf="+xsrf;
var FaTieResult = nhp.PostResultHtml(FaTiePostUrl, cookie, "http://www.zhihu.com/", FaTiePostStr);
}

/// & /// 遍歷CookieContainer
/// &
/// &

& /// &&
public static List& GetAllCookies(CookieContainer cc)
{
List& lstCookies = new List&();

Hashtable table = (Hashtable)cc.GetType().InvokeMember("m_domainTable",
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.GetField |
System.Reflection.BindingFlags.Instance, null, cc, new object[] { });

foreach (object pathList in table.Values)
{
SortedList lstCookieCol = (SortedList)pathList.GetType().InvokeMember("m_list",
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.GetField
| System.Reflection.BindingFlags.Instance, null, pathList, new object[] { });
foreach (CookieCollection colCookies in lstCookieCol.Values)
foreach (Cookie c in colCookies) lstCookies.Add(c);
}
return lstCookies;
}


HTTP 403,伺服器訪問拒絕

這一般都是伺服器主動拒絕你的一個錯誤碼

只能說,你的訪問,被知乎的防採集措施給屏蔽了

你的採集策略,不夠擬人化,呈機器化的採集,這種一般的防封策略都能給你識別出來的

建議你可以用下八爪魚,擬人化的採集試試


有可能被對方網站攔截了


檢查下 UA,token ,Cookie


修改header的X-Forwarded-For欄位偽裝ip


真的是很巧,昨天晚上剛剛遇到了這個問題。原因可能有有很多,我只說自己遇到的,僅供參考,提供一種思路。我爬取的是新浪微博,使用了代理。出現403是因為訪問時網站拒絕,我在瀏覽器上操作也是一樣,隨便看裡面幾個網頁就會出現403,不過刷新幾次就好了。在代碼中實現就是多請求幾次。


你先找個瀏覽器,研究一下request的HTTP Header再來抓


話說介面是怎麼抓到的...為何我用firebug抓不到介面..chrome的network也抓不到介面
話說直接請求followees也可以直接獲取到,剩下的也就是正則了


抓包後發現http://www.zhihu.com/node/ProfileFolloweesListV2 每次遠程獲取 只有offset這個參數發生了變化。其他的諸如header cookie postdata完全返回就OK。


看了樓上的答案,瞬間被鎮住了。大牛真多,不過我建議題主去問問李開復好了~哈哈


推薦閱讀:

為什麼知乎上對「戾氣」有一種特別的定義呢?
如果知乎答題全部實行匿名,會帶來什麼變化?
如何以匿名身份點贊同?
通過知乎學習,靠譜嗎?
知乎有哪些觀點讓你完全改變了之前對此事物認知?

TAG:知乎 | 程序員 | PHP | 爬蟲計算機網路 | 網頁爬蟲 |