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”大家也是能接受的……Python——论一只爬虫的自我修养3:隐藏

Beautiful Soup 是一个可以从 HTML 或 XML 文件中提取数据的 Python 库。它能够通过你喜欢的转换器实现惯用的文档导航,查找,修改文档的方式。Beautiful Soup 会帮你节省数小时甚至数天的工作时间。
这玩意儿到底怎么用?
看这 -> 传送门
上边链接是官方的快速入门教程(不用惧怕,这次有中文版了),请大家花差不多半个小时的时间自学一下,然后完成下边题目。
噢,对了,大家可以使用 pip 安装(Python3.4 以上自带的神一般的软件包管理系统,有了它 Python 的模块安装、卸载全部一键搞定!)
Step One
打开命令行窗口(CMD) -> 输入 py -3 -m pip install BeautifulSoup4 命令 -> 搞定: 

Python——论一只爬虫的自我修养3:隐藏

Step Two
No step two!!!
0. 编写一个爬虫,爬百度百科“网络爬虫”的词条(链接 -> http://baike.baidu.com/view/284853.htm),将所有包含“view”的链接按下边格式打印出来: 

Python——论一只爬虫的自我修养3:隐藏

提示:题目中需要使用到简单的正则表达式(在官方的快速入门教程有演示),如果你希望马上就深入学习正则表达式,当然可以给你预支一下后边的知识 -> 传送门
1. 直接打印词条名和链接不算什么真本事儿,这题要求你的爬虫允许用户输入搜索的关键词。
然后爬虫进入每一个词条,然后检测该词条是否具有副标题(比如搜索“猪八戒”,副标题就是“(中国神话小说《西游记》的角色)”),如果有,请将副标题一并打印出来: 

Python——论一只爬虫的自我修养3:隐藏

程序实现效果如下: 

Python——论一只爬虫的自我修养3:隐藏

2. 哗啦啦地丢一堆链接给用户可不是什么好的体验,我们应该先打印 10 个链接,然后问下用户“您还往下看吗?”

来,我给大家演示下: 

Python——论一只爬虫的自我修养3:隐藏

然后为了增加用户体验,代码需要捕获未收录的词条,并提示: 

Python——论一只爬虫的自我修养3:隐藏

提示:希望你还记得有生成器这么个东东 Python——论一只爬虫的自我修养3:隐藏 

图一时之快先看答案,您将失去一次锻炼的机会!
请先自己思考和动手,再回复查看参考答案!
测试题答案:
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”的链接按下边格式打印出来。
代码清单:

  1. import urllib.request
  2. import re
  3. from bs4 import BeautifulSoup
  4.  
  5. def main():
  6.     url = "http://baike.baidu.com/view/284853.htm"
  7.     response = urllib.request.urlopen(url)
  8.     html = response.read()
  9.     soup = BeautifulSoup(html, "html.parser") # 使用 Python 默认的解析器
  10.     
  11.     for each in soup.find_all(href=re.compile("view")):
  12.         print(each.text, "->", ''.join(["http://baike.baidu.com", each["href"]]))
  13.         # 上边用 join() 不用 + 直接拼接,是因为 join() 被证明执行效率要高很多
  14.  
  15. if __name__ == "__main__":
  16.     main()

复制代码


1. 直接打印词条名和链接不算什么真本事儿,这题要求你的爬虫允许用户输入搜索的关键词。
代码清单:

  1. import urllib.request
  2. import urllib.parse
  3. import re 
  4. from bs4 import BeautifulSoup
  5.  
  6. def main():
  7.     keyword = input("请输入关键词:")
  8.     keyword = urllib.parse.urlencode({"word":keyword})
  9.     response = urllib.request.urlopen("http://baike.baidu.com/search/word?%s" % keyword)
  10.     html = response.read()
  11.     soup = BeautifulSoup(html, "html.parser")
  12.  
  13.     for each in soup.find_all(href=re.compile("view")):
  14.         content = ''.join([each.text])
  15.         url2 = ''.join(["http://baike.baidu.com", each["href"]])
  16.         response2 = urllib.request.urlopen(url2)
  17.         html2 = response2.read()
  18.         soup2 = BeautifulSoup(html2, "html.parser")
  19.         if soup2.h2:
  20.             content = ''.join([content, soup2.h2.text])
  21.         content = ''.join([content, " -> ", url2])
  22.         print(content)
  23.  
  24. if __name__ == "__main__":
  25.     main()

复制代码


2. 哗啦啦地丢一堆链接给用户可不是什么好的体验,我们应该先打印 10 个链接,然后问下用户“您还往下看吗?”
代码清单:

  1. import urllib.request
  2. import urllib.parse
  3. import re 
  4. from bs4 import BeautifulSoup
  5.  
  6. def test_url(soup):
  7.     result = soup.find(text=re.compile("百度百科尚未收录词条"))
  8.     if result:
  9.         print(result[0:-1]) # 百度这个碧池在最后加了个“符号,给它去掉
  10.         return False
  11.     else:
  12.         return True
  13.  
  14. def summary(soup):
  15.     word = soup.h1.text
  16.     # 如果存在副标题,一起打印
  17.     if soup.h2:
  18.         word += soup.h2.text
  19.     # 打印标题
  20.     print(word)
  21.     # 打印简介
  22.     if soup.find(class_="lemma-summary"):
  23.         print(soup.find(class_="lemma-summary").text)   
  24.  
  25. def get_urls(soup):
  26.     for each in soup.find_all(href=re.compile("view")):
  27.         content = ''.join([each.text])
  28.         url2 = ''.join(["http://baike.baidu.com", each["href"]])
  29.         response2 = urllib.request.urlopen(url2)
  30.         html2 = response2.read()
  31.         soup2 = BeautifulSoup(html2, "html.parser")
  32.         if soup2.h2:
  33.             content = ''.join([content, soup2.h2.text])
  34.         content = ''.join([content, " -> ", url2])
  35.         yield content
  36.         
  37.  
  38. def main():
  39.     word = input("请输入关键词:")
  40.     keyword = urllib.parse.urlencode({"word":word})
  41.     response = urllib.request.urlopen("http://baike.baidu.com/search/word?%s" % keyword)
  42.     html = response.read()
  43.     soup = BeautifulSoup(html, "html.parser")
  44.  
  45.     if test_url(soup):
  46.         summary(soup)
  47.         
  48.         print("下边打印相关链接:")
  49.         each = get_urls(soup)
  50.         while True:
  51.             try:
  52.                 for i in range(10):
  53.                     print(next(each))
  54.             except StopIteration:
  55.                 break
  56.             
  57.             command = input("输入任意字符将继续打印,q退出程序:")
  58.             if command == 'q':
  59.                 break
  60.             else:
  61.                 continue
  62.     
  63. if __name__ == "__main__":
  64.     main()