上周末, 我又做了一个爬虫项目 1p3aMSCSAdminReport, 主要是用来爬取一亩三分地(1p3a)研究生CS专业录取情况结果汇总.
虽然这次感觉自己的爬虫技术进步很大, 对比一年多前惨不忍睹的单页面针对公开信息的爬虫, 这次的爬虫又需要登录系统, 又需要切换到不同的页面, 再进入不同标签爬取, 还是比较复杂的.
爬虫的部分没什么说的其实, 只要对 HTML 语言有一点浅薄了解, 再加上一点点的观察能力, 我想实现一个不太复杂的爬虫问题都不大. 毕竟 BeautifulSoup 是那么的好用, 直接选取标签和类名 (class) 或身份 (id) 就能一下子抓取到这个标签, 之后再选择标签的属性, 想要什么内容就能得到什么内容了.
这一篇先讲讲怎么破解需要登录的系统好了.
Cookies模拟登录
1p3a 系统有 Cloudflare 保护, 并且还要登录账号. 否则爬取的结果内的具体信息都是不显示的.
根据掘金的 掘金 - Python 爬虫模拟登录方法汇总, 我们知道大概有三种方法可以模拟登录: POST 请求方法, 添加 Cookies 方法, Selenium 模拟登录.
文章表示添加 Cookies 方法最为简单, 因此我选择这种方式来操作. 文章这一部分写的比较简单, 没有说要怎么获得 Cookies 导致我走了点弯路.
怎么获得 Cookies 呢?
- Firefox: F12进入网络(Network)分页, 重新登录论坛, 查看名为 bbs/ 的请求, 选择请求头(Request Header)分页, 请求头内的 Cookie 即为你的 cookie, 替换全局变量 cookie 即可.
- Chrome: F12进入网络(Network)分页, 重新登录论坛, 查看名为 bbs/ 的请求的请求头(Request Header), 请求头内的 Cookie 即为你的 cookie, 替换全局变量 cookie 即可.
cookie = '记得建一个小号来爬 我的号已经被封了GG'
user_agent = 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36'
# login with cookies
def login(url):
headers = {
'User-Agent': user_agent,
# How to get cookies?
# Chrome: F12 and goto 'Network' page, login and check the Request Header in Name='bbs/', you can get cookie in Request Header section
# Firefox: F12 and goto 'Network' page, login and check cookie page from name='bbs/', copy all cookies. You need to reformat it first from lots of 'xx:"yy"' to 'xx=yy; xxx=yyy'
'Cookie': cookie,
}
session = requests.Session()
response = session.get(url, headers=headers)
response.text.encode('utf-8')
if response.status_code != 200:
print('WARNING: Login Failed!!')
return response
还记得 1p3a 系统有 Cloudflare 保护吗? 我发现只要使用 requests.Session() 而不是 requests.get() 就能轻轻松松绕过 Cloudflare. 突然感觉 Cloudflare 好弱..
BeautifulSoup点津
再来讲讲 BeautifulSoup, 应该是每一个爬虫都会用到的库. 具体用法是从 html 文件中读取指定的标签, 从而获得标签的属性或是内容.
我们先要获得 html 的 raw 格式文本
def get_html_info(url):
response = login(url)
if response.status_code == 200:
return response.text
else:
return response.status_code
再对该文本进行 美味的汤操作
from bs4 import BeautifulSoup as bs
def get_page_data(url):
raw_html_info = get_html_info(url)
if isinstance(raw_html_info, str):
soup = bs(raw_html_info, 'html.parser')
因为上一步如果发生异常, 返回的是错误码而不是字符串, 因此这里先对值的类型做一个判断. 如果是字符串再进行接下来的步骤.
之后如果我们要获得, 例如 id为 123, class为 aaaa 的 a 标签下的href和标签内容.
def get_page_data(url):
raw_html_info = get_html_info(url)
if isinstance(raw_html_info, str):
soup = bs(raw_html_info, 'html.parser')
my_a = soup.find('a', class_='aaaa', id='123', href=True)
a_url = a['href']
a_content = "".join(my_a.contents)
这边新加入了三行代码. 第一行是寻找这个 id为 123, class为 aaaa 的 a 标签, 因为会用到 href, 因此要把 href=True.
第二行是获得 href 的值. 第三行是获得标签的内容, 之所以使用 ““.join() 是因为 my_a.contents 返回值会装在列表中, 使用 join 可以消去列表得到字符串.
其他部分的内容我觉得不是很有内容, 爬虫的源文件在 1p3aMSCSAdminReport, 如果有不明白可以去看看.
参考资料: