本实验任务主要基于ubuntu完成python对单个网页内容的爬取,完成对所需数据的采集。
通过完成本实验任务,要求学生掌握python构建多线爬取网页数据的采集技能,增加学生对python多线的了解,为以后从事数据采集工程师奠定基础。
完成实验实例,python语言爬取网页小说图片。
Ubuntu、Python2.7.12、Tomcat
线程是程序中一个单一的顺序控制流程。进程内有一个相对独立的、可调度的执行单元,是系统独立调度和分派CPU的基本单位指令运行时的程序的调度单位。在单个程序中同时运行多个线程完成不同的工作,称为多线程。
导入线程模块,创建一个以上的线程,就是多线程。
本实验,通过一个for循环去创建线程,循环一次就创建一个,最后一起启动,然后线程把任务进行分工执行。
♥ 知识链接
threading模块
threading 模块提供的常用方法:
threading.currentThread(): 返回当前的线程变量。
threading.enumerate(): 返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的线程。
threading.activeCount(): 返回正在运行的线程数量,与len(threading.enumerate())有相同的结果。
点击桌面【Xfce Terminal】或者鼠标右键点击【Open Terminal Here】打开终端
【cd /simple/tomcat/bin/】进入Tomcat目录下,执行【./startup.sh】开启Tomcat;如图1所示:

图1 开启Tomcat
打开火狐浏览器【 http://localhost: 8080/test/xiaoshuo.html 】验证Tomcat是否启动成功,这也是我们要爬取的网页;如图2所示:

图2 验证Tomcat
再打开一个终端(鼠标右键打开终端,会默认在桌面目录,点击图标Xfce,路径会默认在根目录),【cd /】进入根目录;执行命令【vim ThreadDemo.py】创建py文件,导包和定义url如图3所示:
1. #!/usr/bin/env python
2. # -*- coding:utf-8 -*-
3.
4. import re
5. import time
6. import urllib2
7. import requests
8. import threading
9. import multiprocessing
10. from Queue import Queue
11. from threading import Thread
12. from bs4 import BeautifulSoup
13. import sys
14. reload(sys)
15. sys.setdefaultencoding("utf-8")

图3 导入所需要的包
打开火狐浏览器按【F12】,找UserAgent;如图4所示:

图4 提取UserAgent
在代码中定义头部以及请求信息和解析网页,返回网页内容,并把其封装成一个函数【url_request】,代码如下;如图5所示:
1. #定义个一个容器
2. q = Queue()
3. #函数参数为 url
4. def url_request(url):
5. #定义头部
6. UserAgent = "Mozilla/6.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36"
7. headers = {"User-Agent": UserAgent}
8. #请求
9. req = urllib2.Request(url, headers=headers)
10. html = urllib2.urlopen(req).read()
11. #解析网页
12. content = BeautifulSoup(html, "lxml")
13. #返回网页内容
14. return content

图5 编写UserAgent
定义一个函数【get_img】,参数是图片名字和图片地址,作用是对图片进行下载和保存(存储路径如果不存在请创建或者自定义一个);如图6所示:
1. #传入参数img_name和img_url
2. def get_img(img_name,img_url):
3. #请求img_url
4. img = requests.get(img_url)
5. #保存图片到直接地址
6. with open("/simple/image/%s.png"%img_name,"wb") as fd:
7. for chunk in img.iter_content():
8. fd.write(chunk)
9. fd.close()
10. #返回下载信息
11. return threading.current_thread().name + " " + img_name + "== ----------->>> load-OK"

图6 下载图片
获取要爬取小说名字和介绍的标签;先点击红框中的箭头,选择其中一个小说图片,找到所在的标签,记住图片所在div信息(div class=“book-img-info“)还有img的url信息(src=”./xiaoshuo_files/150”);如图7所示:

图7 获取标签信息
从上图可以看出,在网页中的url不全,需要我们对其进行处理;定义一个函数处理img_url;并且对处理完的url进行请求下载;如图8所示:
1. def work():
2. while True:
3. #从容器中获取数据
4. con= q.get()
5. #定义正则
6. name_re=("./xiaoshuo_files/(.*?)$")
7. #从数据中获取url信息
8. img_name = re.findall(name_re,con.img.get("src"))[0]
9. #拼接url
10. img_url="http://localhost:8080/test/xiaoshuo_files/"+ img_name
11. #调用图片下载函数
12. image = get_img(img_name,img_url)
13. #输出图片下载状态
14. print image
15. q.task_done()

图8 拼接url
定义主模块调,用上面定义的函数,定义3个线程;如图9所示:
1. if __name__ == "__main__":
2. #爬取的url
3. url = "http://localhost:8080/test/xiaoshuo.html"
4. #请求url
5. content=url_request(url)
6. content_data= content.find_all("div",class_="book-img-box")
7. #定义三个线程
8. threadCount = 3
9. #遍历创建线程
10. for i in range(threadCount):
11. t = Thread(target=work)
12. t.daemon = True
13. t.start()
14. #存储数据到容器中
15. for label in content_data:
16. q.put(label)
17. q.join()

图9 下载图片
全部代码如下:
1. #!/usr/bin/env python
2. # -*- coding:utf-8 -*-
3.
4. import re
5. import time
6. import urllib2
7. import requests
8. import threading
9. import multiprocessing
10. from Queue import Queue
11. from threading import Thread
12. from bs4 import BeautifulSoup
13. import sys
14. reload(sys)
15. sys.setdefaultencoding("utf-8")
16.
17. q = Queue()
18.
19. def url_request(url):
20. UserAgent = "Mozilla/6.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36"
21. headers = {"User-Agent": UserAgent}
22. req = urllib2.Request(url, headers=headers)
23. html = urllib2.urlopen(req).read()
24. content = BeautifulSoup(html, "lxml")
25. return content
26.
27. def get_img(img_name,img_url):
28. img = requests.get(img_url)
29. with open("/simple/image/%s.png"%img_name,"wb") as fd:
30. for chunk in img.iter_content():
31. fd.write(chunk)
32. fd.close()
33. return threading.current_thread().name + " " + img_name + "== ----------->>> load-OK"
34.
35.
36. def work():
37. while True:
38. con= q.get()
39. name_re=("./xiaoshuo_files/(.*?)$")
40. img_name = re.findall(name_re,con.img.get("src"))[0]
41. img_url="http://localhost:8080/test/xiaoshuo_files/"+ img_name
42. image = get_img(img_name,img_url)
43. print image
44. q.task_done()
45.
46. if __name__ == "__main__":
47. url = "http://localhost:8080/test/xiaoshuo.html"
48. content=url_request(url)
49. content_data= content.find_all("div",class_="book-img-box")
50. threadCount = 3
51. for i in range(threadCount):
52. t = Thread(target=work)
53. t.daemon = True
54. t.start()
55.
56. for label in content_data:
57. q.put(label)
58. q.join()
保存代码(注意代码保存目录),因系统原因为了避免乱码,我们修改终端编码为(utf-8)暂时识别中文;如图10所示:

图10 修改终端编码
通过鼠标右键打开终端【cd /】进图根目录,执行【Python ThreaDemo.py】;如图11所示:

图11 运行结果
代码运行成功,我们把数据保存到/simple/image/目录下;打开文件系统,进入到目录下,查看如图12所示:

图12 查看结果
♥ 温馨提示
在使用多线程时要注意线程安全。
什么是线程安全?如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。