Python——论一只爬虫的自我修养3:隐藏
测试题:
0. 服务器是如何识访问来自浏览器还是非浏览器的?
1. 明明代码跟视频中的栗子一样,一运行却出错了,但在不修改代码的情况下再次尝试运行却又变好了,这是为什么呢?
2. Request 是由客户端发出还是由服务端发出?
3. 请问如何为一个 Request 对象动态的添加 headers?
4. 简单来说,代理服务器是如何工作的?他有时为何不工作了?
5. HTTP 有好几种方法(GET,POST,PUT,HEAD,DELETE,OPTIONS,CONNECT),请问你如何晓得 Python 是使用哪种方法访问服务器呢?
6. 上一节课后题中有涉及到登陆问题,辣么,你还记得服务器是通过什么来确定你是登陆还是没登陆的么?他会持续到什么时候呢?
动动手:
小甲鱼打算在这里先给大家介绍一个压箱底的模块 —— Beautiful Soup 4
翻译过来名字有点诡异:漂亮的汤?美味的鸡汤?呃……
好吧,只要你写出一个普罗大众都喜欢的模块,你管它叫“Beautiful Shit”大家也是能接受的……
Beautiful Soup 是一个可以从 HTML 或 XML 文件中提取数据的 Python 库。它能够通过你喜欢的转换器实现惯用的文档导航,查找,修改文档的方式。Beautiful Soup 会帮你节省数小时甚至数天的工作时间。
这玩意儿到底怎么用?
看这 -> 传送门
上边链接是官方的快速入门教程(不用惧怕,这次有中文版了),请大家花差不多半个小时的时间自学一下,然后完成下边题目。
噢,对了,大家可以使用 pip 安装(Python3.4 以上自带的神一般的软件包管理系统,有了它 Python 的模块安装、卸载全部一键搞定!)
Step One:
打开命令行窗口(CMD) -> 输入 py -3 -m pip install BeautifulSoup4 命令 -> 搞定:
Step Two:
No step two!!!
0. 编写一个爬虫,爬百度百科“网络爬虫”的词条(链接 -> http://baike.baidu.com/view/284853.htm),将所有包含“view”的链接按下边格式打印出来:
提示:题目中需要使用到简单的正则表达式(在官方的快速入门教程有演示),如果你希望马上就深入学习正则表达式,当然可以给你预支一下后边的知识 -> 传送门
1. 直接打印词条名和链接不算什么真本事儿,这题要求你的爬虫允许用户输入搜索的关键词。
然后爬虫进入每一个词条,然后检测该词条是否具有副标题(比如搜索“猪八戒”,副标题就是“(中国神话小说《西游记》的角色)”),如果有,请将副标题一并打印出来:
程序实现效果如下:
2. 哗啦啦地丢一堆链接给用户可不是什么好的体验,我们应该先打印 10 个链接,然后问下用户“您还往下看吗?”
来,我给大家演示下:
然后为了增加用户体验,代码需要捕获未收录的词条,并提示:
提示:希望你还记得有生成器这么个东东
图一时之快先看答案,您将失去一次锻炼的机会!
请先自己思考和动手,再回复查看参考答案!
测试题答案:
0. 服务器是如何识访问来自浏览器还是非浏览器的?
答:通过发送的 HTTP 头中的 User-Agent 来进行识别浏览器与非浏览器,服务器还以 User-Agent 来区分各个浏览器。
1. 明明代码跟视频中的栗子一样,一运行却出错了,但在不修改代码的情况下再次尝试运行却又变好了,这是为什么呢?
答: 在网络信息的传输中会出现偶然的“丢包”现象,有可能是你发送的请求服务器没收到,也有可能是服务器响应的信息不能完整送回来……尤其在网络阻塞的时候。所以,在设计一个“称职”的爬虫时,需要考虑到这偶尔的“丢包”现象。
2. Request 是由客户端发出还是由服务端发出?
答:我们之前说 HTTP 是基于“请求-响应”模式,Request 即请求的意思,而 Response 则是响应的意思。由客户端首先发出 Request,服务器收到后返回 Response。
3. 请问如何为一个 Request 对象动态的添加 headers?
答:add_header() 方法往 Request 对象添加 headers。
4. 简单来说,代理服务器是如何工作的?他有时为何不工作了?
答: 将信息传给代理服务器,代理服务器替你向你要访问的服务器发送请求,然后在将服务器返回的内容返回给你。
因为有“丢包”现象发生,所以多了一个中间人就意味着会多一层发生“丢包”的几率,且大多数代理并不只为一个人服务,尤其是免费代理。
PS:大家想做“坏坏”的事情时可以考虑多几层代理,一般来说路由器日志并不会保存很长时间,几层代理后,基本很难查到是谁请求的。
5. HTTP 有好几种方法(GET,POST,PUT,HEAD,DELETE,OPTIONS,CONNECT),请问你如何晓得 Python 是使用哪种方法访问服务器呢?
答:使用 get_method() 方法获取 Request 对象具体使用哪种方法访问服务器。最常用的无非就是 GET 和 POST 了,当 Request 的 data 参数被赋值的时候,get_method() 返回 'POST',否则一般情况下返回 'GET'。
6. 上一节课后题中有涉及到登陆问题,辣么,你还记得服务器是通过什么来确定你是登陆还是没登陆的么?他会持续到什么时候呢?
答: 是 cookie,服务器通过判断你提交的 cookie 来确定访问是否来自”熟人“。
简单来说 cookie 可以分成两类:
- 一类是即时过期的 cookies,称为“会话” cookies,当浏览器关闭时(这里是 python 的请求程序)自动清除
- 另一类是有期限的 cookies,由浏览器进行存储,并在下一次请求该网站时自动附带(如果没过期或清理的话)
动动手答案:
0. 编写一个爬虫,爬百度百科“网络爬虫”的词条(链接 -> http://baike.baidu.com/view/284853.htm),将所有包含“view”的链接按下边格式打印出来。
代码清单:
- import urllib.request
- import re
- from bs4 import BeautifulSoup
- def main():
- url = "http://baike.baidu.com/view/284853.htm"
- response = urllib.request.urlopen(url)
- html = response.read()
- soup = BeautifulSoup(html, "html.parser") # 使用 Python 默认的解析器
- for each in soup.find_all(href=re.compile("view")):
- print(each.text, "->", ''.join(["http://baike.baidu.com", each["href"]]))
- # 上边用 join() 不用 + 直接拼接,是因为 join() 被证明执行效率要高很多
- if __name__ == "__main__":
- main()
复制代码
1. 直接打印词条名和链接不算什么真本事儿,这题要求你的爬虫允许用户输入搜索的关键词。
代码清单:
- import urllib.request
- import urllib.parse
- import re
- from bs4 import BeautifulSoup
- def main():
- keyword = input("请输入关键词:")
- keyword = urllib.parse.urlencode({"word":keyword})
- response = urllib.request.urlopen("http://baike.baidu.com/search/word?%s" % keyword)
- html = response.read()
- soup = BeautifulSoup(html, "html.parser")
- for each in soup.find_all(href=re.compile("view")):
- content = ''.join([each.text])
- url2 = ''.join(["http://baike.baidu.com", each["href"]])
- response2 = urllib.request.urlopen(url2)
- html2 = response2.read()
- soup2 = BeautifulSoup(html2, "html.parser")
- if soup2.h2:
- content = ''.join([content, soup2.h2.text])
- content = ''.join([content, " -> ", url2])
- print(content)
- if __name__ == "__main__":
- main()
复制代码
2. 哗啦啦地丢一堆链接给用户可不是什么好的体验,我们应该先打印 10 个链接,然后问下用户“您还往下看吗?”
代码清单:
- import urllib.request
- import urllib.parse
- import re
- from bs4 import BeautifulSoup
- def test_url(soup):
- result = soup.find(text=re.compile("百度百科尚未收录词条"))
- if result:
- print(result[0:-1]) # 百度这个碧池在最后加了个“符号,给它去掉
- return False
- else:
- return True
- def summary(soup):
- word = soup.h1.text
- # 如果存在副标题,一起打印
- if soup.h2:
- word += soup.h2.text
- # 打印标题
- print(word)
- # 打印简介
- if soup.find(class_="lemma-summary"):
- print(soup.find(class_="lemma-summary").text)
- def get_urls(soup):
- for each in soup.find_all(href=re.compile("view")):
- content = ''.join([each.text])
- url2 = ''.join(["http://baike.baidu.com", each["href"]])
- response2 = urllib.request.urlopen(url2)
- html2 = response2.read()
- soup2 = BeautifulSoup(html2, "html.parser")
- if soup2.h2:
- content = ''.join([content, soup2.h2.text])
- content = ''.join([content, " -> ", url2])
- yield content
- def main():
- word = input("请输入关键词:")
- keyword = urllib.parse.urlencode({"word":word})
- response = urllib.request.urlopen("http://baike.baidu.com/search/word?%s" % keyword)
- html = response.read()
- soup = BeautifulSoup(html, "html.parser")
- if test_url(soup):
- summary(soup)
- print("下边打印相关链接:")
- each = get_urls(soup)
- while True:
- try:
- for i in range(10):
- print(next(each))
- except StopIteration:
- break
- command = input("输入任意字符将继续打印,q退出程序:")
- if command == 'q':
- break
- else:
- continue
- if __name__ == "__main__":
- main()