模拟登陆并爬取Github
前言:如果希望爬取GitHub上所关注的人的最近动态或者你的个人信息(如name,email)我们就需要模拟登陆去爬取信息
- 分析登陆过程
需要探究后台的登陆请求是怎样发送的,登陆之后又有怎么样的处理过程
先打开GitHub的登陆页面,链接是 https://github.com/login
输入用户名和密码,打开开发者工具,把Preserve Log选项勾选上,这表示显示持续日志
然后点击登陆按钮,这时就会看到开发者工具下各个请求过程。
点击session请求
可以看到请求的URL为 https://github.com/session ,请求方式为POST
然后观察Form Data 和Headers这两部分
Headers包含了Cookies、Host、Origin、Referer、User-Agent等信息。Form Data 包含了5个字段,commit是固定的字符串Sign in,anthenticity_token初步判断是一个Base64加密的字符串。login是登录的用户名,password是登录密码
综上所述,我们无法直接构造的内容有Cookies 和 authenticity_token
在登录之前我们会访问到一个登录页面,此页面是通过GET形式访问的。输入用户名密码,点击登陆按钮,浏览器会发送这两部分信息,也就是说Cookies和authenticity_token一定是在访问登录页的时候设置的。
这时候再退出登陆,回到登录页,同时清空Cookies,重新访问登录页,截获发生的请求
Response Headers有一个Set-Cookies字段。这就是设置Cookies的过程
另外我们发现,Response Headers没有相关authenticity_token的信息。所以很有可能authenticity_token还隐藏在其他的地方或者是被计算出来的。我们再从网页的源码去搜索相关的字段,发现源码里面隐藏着这个字段,它是一个隐藏式表单元素。
OK!我们已经获取到所有的信息了
#Xpath版本
import requests, re
from lxml import etree
#定义一个Login类初始变量,其中最重要的变量就是requests库里的Session,它可以帮助我们维持会话,而且自动处理Cookies
class Login(object):
def __init__(self):
self.headers = {
'Referer': 'https://github.com/',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36',
'Host': 'github.com'
}
self.login_url = 'https://github.com/login'
self.post_url = 'https://github.com/session'
self.feed_url = 'https://github.com/dashboard-feed'
self.logined_url = 'https://github.com/settings/profile'
self.session = requests.Session()
#用Seesion对象的get()方法去访问Github的登陆页面,然后用Xpath去解析出登陆所需的authenticity_token信息并返回
def token(self):
response = self.session.get(self.login_url, headers=self.headers)
selector = etree.HTML(response.text)
token = selector.xpath('//div//input[1]/@value')[0]
return token
#已经获取初始的Cookies和authenticity_token,构造一个表单,复制各个字段,其中email和password以变量的形式传递,然后用Session对象的POST()方法模拟登录即可。
def login(self, email, password):
post_data = {
'commit': 'Sign in',
'utf8': '✓',
'authenticity_token': self.token(),
'login': email,
'password': password
}
response = self.session.post(self.post_url, data=post_data, headers=self.headers)
response = self.session.get(self.feed_url, headers=self.headers)
if response.status_code == 200:
self.dynamics(response.text)
# print('response.text', response.text)
response = self.session.get(self.logined_url, headers=self.headers)
if response.status_code == 200:
# print(response.text)
self.profile(response.text)
#
def dynamics(self, html):
print('*'*10+'dynamicing'+'*'*10)
selector = etree.HTML(html)
# print("*"*20, etree.tostring(selector).decode('utf-8'))
# print(selector.xpath('//div[@class="d-flex flex-items-baseline"]'))
dynamics = selector.xpath('//div[@class="d-flex flex-items-baseline"]//div')
# print(dynamics)
for item in dynamics:
etree.strip_elements(item, 'span')
dynamic = ' '.join(item.xpath('.//text()')).replace('\n', ' ').strip()
dynamic = re.sub(' +', ' ', dynamic)
print(dynamic)
print('*' * 10 + 'dynamic end' + '*' * 10)
def profile(self, html):
print('*'*10+'profileing'+'*'*10)
selector = etree.HTML(html)
name = selector.xpath('//input[@id="user_profile_name"]/@value')[0]
email = selector.xpath('//select[@id="user_profile_email"]/option[@value!=""]/text()')[0]
print(name, email)
if __name__ == '__main__':
login = Login()
login.login(email='email',password="password')
Pyquery版本
import requests
from pyquery import PyQuery as pq
class Login(object):
def __init__(self):
self.headers = {
'Referer': 'https://github.com/',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36',
'Host': 'github.com'
}
self.login_url = 'https://github.com/login'
self.post_url = 'https://github.com/session'
self.feed_url = 'https://github.com/dashboard-feed'
self.logined_url = 'https://github.com/settings/profile'
## 维持会话,自动处理cookies
self.session = requests.Session()
## 解析出登录所需要的
def token(self):
response = self.session.get(self.login_url, headers=self.headers)
selector = pq(response.text)
token = selector('input[name="authenticity_token"]').attr('value')
return token
def login(self, email, password):
# print(self.token())
post_data = {
'commit': 'Sign in',
'utf8': '✓',
'authenticity_token': self.token(),
'login': email,
'password': password
}
response = self.session.post(self.post_url, data=post_data, headers=self.headers)
response = self.session.get(self.feed_url, headers=self.headers)
if response.status_code == 200:
self.dynamics(response.text)
# print(response.text)
response = self.session.get(self.logined_url, headers=self.headers)
if response.status_code == 200:
self.profile(response.text)
## 关注人的动态信息
def dynamics(self, html):
selector = pq(html)
# print(selector.text())
dynamics = selector('div[class="d-flex flex-items-baseline"] div')
dynamics.find('span').remove()
# print(dynamics.text())
for item in dynamics.items():
dynamic = item.text().strip()
print(dynamic)
## 详情页面
def profile(self, html):
selector = pq(html)
# print(selector.text())
name = selector('input[id="user_profile_name"]').attr('value')
email = selector('select[id="user_profile_email"] option[selected="selected"]').text()
print(name, email)
if __name__ == '__main__':
login = Login()
login.login(email='email',password='password')
登陆成功相当于建立了一个Session会话,Session对象维护着Cookies的信息,直接请求就会得到模拟登录成功后的页面。