输入关键字 抓取所有网页(《Python爬虫:模拟登录知乎》分析页面请求准备准备)

优采云 发布时间: 2022-01-31 19:27

  输入关键字 抓取所有网页(《Python爬虫:模拟登录知乎》分析页面请求准备准备)

  接上一篇文章《Python爬虫:模拟登录知乎》。

  分析页面请求

  LZ 将从 知乎 的主题页面中获取所有主题及其结构。页面获取主题的ajax请求有两种,分别是“显示子主题”和“加载更多”,都是一次输出10个主题。请求网址如下:

  https://www.zhihu.com/topic/19776749/organize/entire?child=&parent=19552706

  其中 child 和 parent 是主题的 ID,“显示子主题”只需要 parent 参数,“加载更多”需要两个参数。如果两个参数都不存在,则表示“根主题”。

  两种请求的响应数据都是json格式,结构类似,只是msg中的最后一个元素不同。

  抓取逻辑

  知乎主题结构是分层的。为了描述方便,“根主题”的子主题称为一级主题,一级主题的子主题为二级主题,以此类推……

  为了抓取所有主题,流程应该是:从根主题开始,抓取所有一级主题并存储,然后依次分析每个一级主题,如果有子主题,则抓取它的所有子主题(次要)和存储,然后......循环继续下去。

  根据流程,需要实现以下三个主要功能:

  多谈谈主题存储。因为LZ要保存主题结构,“加载更多”时需要使用父主题ID,所以LZ将每个主题记录为一个列表,其中收录四个元素:主题名称、主题ID、父主题ID、有无状态子主题(bool类型),如['entity', '19778287', '19776749', 1],去重时,四个元素相同,则认为是重复。

  代码

  在代码中,第一段是模拟登录知乎。运行时,输入验证码后取数据,完成后数据保存到本地文件。因为运行时间比较长(1小时左右),所以增加了一些状态提示。

  # all_topics.py

import requests

from bs4 import BeautifulSoup

# session变量

s = requests.Session()

headers = {

"Accept": "*/*",

"Accept-Encoding": "gzip,deflate",

"Accept-Language": "en-US,en;q=0.8,zh-TW;q=0.6,zh;q=0.4",

"Connection": "keep-alive",

"Content-Type":" application/x-www-form-urlencoded; charset=UTF-8",

"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2125.111 Safari/537.36",

"Referer": "http://www.zhihu.com/"

}

_xsrf = ''

def set_xsrf():

global _xsrf

html = s.get('http://www.zhihu.com/', headers=headers) # GET请求,获取一个响应对象

soup = BeautifulSoup(html.text,'lxml-xml') # 使用BeautifulSoup解析HTML

_xsrf = soup.findAll(type='hidden')[0]['value']

def get_captcha():

# 获取验证码图片并保存在本地

captcha_url = 'http://www.zhihu.com/captcha.gif'

captcha = s.get(captcha_url, stream=True, headers=headers)

with open('captcha.gif', 'wb') as f:

for line in captcha.iter_content(10):

f.write(line)

f.close()

# 输入获取到的验证码,并入变量captch_str

print('输入验证码:', end='')

captcha_str = input()

return captcha_str.strip()

# 登录请求

def login():

global s

global _xsrf

set_xsrf()

captcha = get_captcha()

data = {

'_xsrf': _xsrf,

'email': '654408619@qq.com',

'password': 'mftx1029',

'remember_me': True,

'captcha': captcha

}

r = s.post(url='http://www.zhihu.com/login/email', data=data, headers=headers)

return r

all_topics = [['根话题', '19776749', '', 1]] # 保存所有的话题,初始状态保存根话题

has_children = [['根话题', '19776749', '', 1]] # 有子话题,但还没有抓取其子话题。初始状态保存根话题

cnt_a = 0 # all_topics元素计数

cnt_h = 0 # has_children元素计数

# 获取一次request的json数据

def get_json(child_id='', parent_id=''):

global s

global headers

url = 'https://www.zhihu.com/topic/19776749/organize/entire'

data = {'child': child_id, 'parent': parent_id, '_xsrf': _xsrf}

res = s.post(url,data=data, headers=headers)

json_dict = res.json()

return json_dict

# 解析json数据,获取子话题及「加载更多」的信息

def json_process(json_dict):

single_dict = {} # 保存下面的children和more

children = [] # 保存子话题

more = [] # 保存加载更多的信息

parent_id = json_dict['msg'][0][2]

if len(json_dict['msg'][1]) == 11:

more_child_id = json_dict['msg'][1][9][0][2]

more = [more_child_id, parent_id]

for i in json_dict['msg'][1][:10]:

child_name, child_id = i[0][1], i[0][2]

if i[1]:

children.append([child_name, child_id, parent_id, 1])

else:

children.append([child_name, child_id, parent_id, 0])

single_dict['children'] = children

single_dict['more'] = more

return single_dict

# 获取一个话题的所有子话题

def get_children(parent_id='', parent_name=''):

global cnt_a

global cnt_h

children = []

more = ['', parent_id]

while more:

json_dict = get_json(child_id=more[0], parent_id=more[1])

single_dict = json_process(json_dict)

children += single_dict['children']

more = single_dict['more']

# 用140个空格清除上次的提示

print(' '*140, end='\r')

# 终端提示抓取状态

print('已获取{}个话题。还有{}个话题排队当中,正在抓取「{}」的第{} 个子话题...'.format(\

cnt_a, cnt_h, str(parent_name).encode('gbk', 'ignore').decode('gbk'), len(children)), end='\r')

return children

# 工作函数,抓取全部话题并保存为文件

def work():

global cnt_h

global cnt_a

login()

while has_children:

first_child = has_children.pop(0)

children = get_children(first_child[1],first_child[0])

for c in children:

# 去重并添加到all_topics和has_topics,后者需要再判断下是否有子话题

if c not in all_topics:

all_topics.append(c)

if c[-1] == 1:

has_children.append(c)

cnt_a = len(all_topics)

cnt_h = len(has_children)

# 存入文件

for item in all_topics:

with open('all_topics.txt', 'a', encoding='utf-8') as f:

string = str(item[0]) + '\t' + str(item[1]) + '\t' + str(item[2]) + '\t' + str(item[3]) + '\n'

f.write(string)

if __name__ == "__main__":

work()

  运行状态如下:

  E:\@coding\python>python all_topics.py

输入验证码:hbfu

已获取53个话题。还有46个话题排队当中,正在抓取「「未归类」话题」的第1600 个子话题...

  LZ是昨天运行的一个程序(2016-02-04),一共捕获了46272个话题。

0 个评论

要回复文章请先登录注册


官方客服QQ群

微信人工客服

QQ人工客服


线