Python5--下载知乎关注者头像

这次这玩意折腾我了半天,终于成功了/(ㄒoㄒ)/~~。急需把中间遇到的问题详细记录下来宣泄一下


在前面代码的基础上,想尝试将知乎上关注某话题的用户头像爬取下来。在参考了知乎大神们的回答之后,修改完善了自己的代码,终于可以运行成功了。相比较前面的内容,其实只多了一个offset概念(就是知乎用来加载后续用户头像的插件?每次加载20个,通过post传递)。其他就是requests,bs4的内容。但不得不说,知乎比糗百折腾多了,在享受了无数的403之后才最终把头像下载下来╮(╯▽╰)╭

需要引用的包

# coding : UFT-8
import requests
import random
import time
import os
import os.path
from bs4 import BeautifulSoup
try:
    import cookielib
except:
    import http.cookiejar as cookielib
from PIL import Image

本来快写完了的。。。结果手贱关了(蓝瘦,香菇),只能从来。requests是用来完成HTTP协议的;bs4是用来解析提取抓取页面的信息;cookies是用来处理cookies文件;PIL用来处理验证码图片。

基本信息设置

#该程序是爬取知乎美妆话题下关注人头像
#登录知乎
#报文头设置!
headers = {
    'Accept': '*/*',
    'Accept-Encoding': 'gzip, deflate',
    'Accept-Language': 'zh-CN,zh;q=0.8',
    'Connection': 'keep-alive',
    'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
    'Host': 'www.zhihu.com',
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.87 Safari/537.36',
    'X-Requested-With': 'XMLHttpRequest'
}
#知乎主页
HostUrl = 'http://www.zhihu.com'
#摘取信息的页面
FollowerUrl = 'https://www.zhihu.com/question/22390958/followers'
#登录url
LoginUrl = 'https://www.zhihu.com/login/email'
timeout = random.choice(range(60, 180))

报文头很重要!如果访问时不添加或者设计的简单很有可能会被知乎403。。。这里可以使用Chrome的F12开发者项目来查看真正浏览器访问时的报文头,从而参考设计自己的报文头。

session以及cookies

session = requests.session()#创建一个session

#使用cookie信息加载
session.cookies = cookielib.LWPCookieJar(filename='cookies')
try:
    session.cookies.load(ignore_discard=True)
except:
    print("cookie加载失败")

这里大改意思就是创建一个会话,然后将保存到本地的cookies文件读入。如果读入成功,则可以实现免登陆;若失败则需要输入用户信息进行登录。关于cookies的处理,今后再看(=_=网上抄的)

知乎登录

这里和前面一节基本相同,只是对部分函数进行了简单的修改

  • 测试是否已经登录
def isLogin():
    # 通过查看用户个人信息来判断是否已经登录
    url = "https://www.zhihu.com/settings/profile"
    login_code = session.get(url,allow_redirects=False,headers = headers).status_code
    print(login_code)
    if int(x=login_code) == 200:
        return True
    else:
        return False

再加载完cookies后,访问用户个人信息。若访问成功说明登录成功;否则重新登录。

  • 用户验证登录
#验证登录
def login(url):
    login_data = {
        '_xsrf' : get_xsrf(url),
        'password' : '密码(⊙o⊙)',
        'remember_me' : 'true',
        'email' : '邮箱~\(≧▽≦)/~'
    }
    try:#不需要验证码登录
        repr=session.post(LoginUrl, data=login_data, headers=headers)
        print(repr)
    except:#需要验证码登录
        login_data['captcha_type'] = 'cn'
        login_data['captcha'] = get_captcha()
        repr = session.post(LoginUrl,data=login_data,headers=headers)
        print(repr)
    session.cookies.save()

这里分两种登录情况,可以通过Chrome查看两种情况下需要post的信息。需要验证码登录时,post的数据会加上captcha_type和captcha,其中captcha是用户输入的验证码。另外!!_xsrf在整个过程中很重要!!这里从Chrome浏览器的F12可以看到需要post该信息,但到后面抓取信息时就变成了磨人的小妖精/(ㄒoㄒ)/~~。在登录成功后,将cookies保存到本地,方便下一次免登录。

获取_xsrf

#获取_xsrf
def get_xsrf(url):
    res = session.get(url,headers = headers,timeout=timeout).content
    _xsrf = BeautifulSoup(res,'html.parser').find('input',attrs={'name':'_xsrf'})['value']
    print(_xsrf)
    return _xsrf

和前面一节知乎登录获取方式没啥区别,只是单独提出做了一个函数,方便后面程序的调用。

解析验证码

#解析验证码
def get_captcha():
    t = str(int(time.time() * 1000))
    captcha_url = 'http://www.zhihu.com/captcha.gif?r=' + t + "&type=login"
    r = session.get(captcha_url, headers=headers)
    with open('captcha.jpg', 'wb') as f:
        f.write(r.content)
        f.close()
    # 用pillow 的 Image 显示验证码
    # 如果没有安装 pillow 到源代码所在的目录去找到验证码然后手动输入
    try:
        im = Image.open('captcha.jpg')
        im.show()
        im.close()
    except:
        print(u'请到 %s 目录找到captcha.jpg 手动输入' % os.path.abspath('captcha.jpg'))
    captcha = input("please input the captcha\n>")
    return captcha

获取用户头像

这里就是折腾我最久的地方,收到了无数的403╮(╯▽╰)╭!知乎用offset加载用户的头像,每次加载20个,使用的post传递数据。

  • 获取页面内容
#加载页面获取html内容,主要offset
def get_html(_xsrf):
    url = FollowerUrl
    res = session.get(url,headers=headers)#进入美妆话题
    pictures = get_pic(res.text)#获取当前页面头像
    download_pic(pictures)#下载头像
    time.sleep(random.choice(range(2,5)))#睡眠函数,防止爬取过快封ip
    print(_xsrf)
    headers['X-Xsrftoken'] = _xsrf
    headers['Referer'] = url
    for x in range(20,100,20):#x从20-100,每次加20,即20,40,60,80,100
    #需要post的值
        form_data = {
            'start' : '0',
            'offset' : str(x),
            '_xsrf' : _xsrf
        }
        print(str(x))
        try:
            res= session.post(url,data=form_data,headers=headers)
            print(res)
        except:
            print("offset加载失败")
        else:
            content = res.text
            html = eval(content)['msg'][1]#获取服务器返回的值
            pictures = get_pic(html)
            download_pic(pictures)
        time.sleep(random.choice(range(1,5)))

这里有几点需要注意:

  • post的值需要包含_xsrf,这个用Chrome查看时是看不到有这个数据传输的,只有start和offset(简直心机(#‵′)凸)。刚开始只包含了start和offset进行post,总是返回403。然后看到报文头里面有xsrf的值,就试着带着这玩意传递了,竟然真是这个原因!(艹皿艹 )
  • 使用cookies登录方式,这里传递的form_data中的_xsrf并不是登录时的_xsrf值。在传递数据包含了_xsrf后,使用用户输入信息登录的方式可以访问到结果。但是用cookies登录方式,这里post总是返回403。之前在我用Chrome查看时,发现使用cookies登录方式后访问该页面的_xsrf值与登录时_xsrf一致,因此我错误的将登录时获取到的_xsrf值像cookies一样以文件的方式保存下来了,当用cookies方式登录时,就从本地读取_xsrf值放入form_data中。但是!!根本不是这样,使用cookies登录后,需要重新获取_xsrf的值!根本不是登录时的值。不知道为什么和浏览器中看到的不一样/(ㄒoㄒ)/~~。这里真的折腾了好久呀。
  • 这里post成功后返回的值并不是整个页面,而仅仅是这20个头像那一部分的内容,而且返回内容和get方式返回还是有点区别的。post的返回内容有点类似于dict,但不是一个dict,需要使用eval()函数格式化一下。

  • 解析获取的html
#根据content内容提取头像
def get_pic(content):
    pictures = []
    bs4 = BeautifulSoup(content, "html.parser")
    users = bs4.find_all(class_='zm-profile-card zm-profile-section-item zg-clear no-hovercard')#查找所有关注者
    for user in users:
        temp = []
        username = user.find('a').get('title')#查找关注者姓名
        temp.append(username)
        picUrl = user.find('img').get('src')#查找用户头像url
        picUrl = picUrl.replace('\\','')
        temp.append(picUrl)
        print(temp)
        pictures.append(temp)
    return pictures

这里需要注意的就是post返回的url含了“\”符号,需要把这个去掉才是正确的url。

  • 下载头像
def download_pic(pictures):
    if not os.path.exists('pic'):
        os.makedirs('pic')
    for picture in pictures:
        try:
            r= requests.get(picture[1],headers)
        except:
            print("图片下载失败")
        else:
            filename = picture[0]+'.jpg'
            path = "pic/" + filename
            with open(path,'wb') as f:
                f.write(r.content)
        time.sleep(random.choice(range(1,3)))

与前面下载糗百头像代码基本一样

主函数

if __name__ == '__main__':
    if isLogin():
        print("已经登录")
    else:
        print("重新登录")
        login(HostUrl)
    _xsrf = get_xsrf(HostUrl)
    get_html(_xsrf)

这里放上源代码(⊙o⊙)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值