【Python数据分析】Python3多线程并发网络爬虫-以豆瓣图书Top250为例
基于上两篇文章的工作
【Python数据分析】Python3操作Excel-以豆瓣图书Top250为例
【Python数据分析】Python3操作Excel(二) 一些问题的解决与优化
已经正确地实现豆瓣图书Top250的抓取工作,并存入excel中,但是很不幸,由于采用的串行爬取方式,每次爬完250页都需要花费7到8分钟,显然让人受不了,所以必须在效率上有所提升才行。
仔细想想就可以发现,其实爬10页(每页25本),这10页爬的先后关系是无所谓的,因为写入的时候没有依赖关系,各写各的,所以用串行方式爬取是吃亏的。显然可以用并发来加快速度,而且由于没有同步互斥关系,所以连锁都不用上。
既然考虑并发,那么就有多进程和多线程两种方式,各自的优缺点比较可以见:这里
简单来说,多进程稳定,因为一个进程挂掉其他进程不受影响,但是开销大,建立太多进程会消耗系统大量资源,并且切换慢,因为要通过系统进程调度。
多线程作为“轻量级的进程”,是操作系统调度的基本单位,切换快速,只消耗极少的资源,但是缺点就是一个线程崩掉整个进程包括其他线程都会崩掉,所以稳定性欠佳。
这里虽然进程数/线程数很少(只有10个),即使采用多进程也不会有多大的开销,但是为了更快地爬取,且爬取豆瓣这样的大站,稳定性不会太差,所以还是采用多线程比较实惠。
多线程有两个模块,一个Thread模块,一个threading模块,但是前者现在用的很少了,后者更加方便实用。所以采用后者。
在程序中实用线程有两种方法,一种是自己写一个class,并重写此class中的__init__方法和run()方法,创建一个这个class的对象并调用start()时run()方法自动调用。另一种是在threading.Thread构造函数中传入要用线程运行的函数及其参数。我采用的是后者。
多线程主代码如下:
thread = [] for i in range(0,250,25):
geturl = url + "/start=" + str(i) #要获取的页面地址
print("Now to get " + geturl)
t = threading.Thread(target=crawler,
args=(s,i,url,header,image_dir,worksheet,txtfile))
thread.append(t) for i in range(0,10):
thread[i].start() for i in range(0,10):
thread[i].join()
以前的爬取和存储函数写到了crawler中,具有7个参数。将10页的url都放入thread列表中,然后逐个启动,启动后调用join()等待每一个线程结束,如果不等待的话,会发现有的已经运行到下面关闭文件了,那么别的没运行完的就写不了了。
更改和简化后的全部代码如下:
# -*- coding:utf-8 -*-
import requests
import re
import xlsxwriter
from bs4 import BeautifulSoup
from datetime import datetime
import codecs
import threading #下载图片
def download_img(imageurl,image_dir,imageName = "xxx.jpg"):
rsp = requests.get(imageurl, stream=True)
image = rsp.content
path = image_dir + imageName +'.jpg'
with open(path,'wb') as file:
file.write(image) def crawler(s,i,url,header,image_dir,worksheet,txtfile):
postData = {"start":i} #post数据
res = s.post(url,data = postData,headers = header) #post
soup = BeautifulSoup(res.content.decode(),"html.parser") #BeautifulSoup解析
table = soup.findAll('table',{"width":"100%"}) #找到所有图书信息的table
sz = len(table) #sz = 25,每页列出25篇文章
for j in range(1,sz+1): #j = 1~25
sp = BeautifulSoup(str(table[j-1]),"html.parser") #解析每本图书的信息 imageurl = sp.img['src'] #找图片链接
bookurl = sp.a['href'] #找图书链接
bookName = sp.div.a['title']
nickname = sp.div.span #找别名
if(nickname): #如果有别名则存储别名否则存’无‘
nickname = nickname.string.strip()
else:
nickname = "" notion = str(sp.find('p',{"class":"pl"}).string) #抓取出版信息,注意里面的.string还不是真的str类型
rating = str(sp.find('span',{"class":"rating_nums"}).string) #抓取平分数据
nums = sp.find('span',{"class":"pl"}).string #抓取评分人数
nums = nums.replace('(','').replace(')','').replace('\n','').strip()
nums = re.findall('(\d+)人评价',nums)[0]
download_img(imageurl,bookName) #下载图片
book = requests.get(bookurl) #打开该图书的网页
sp3 = BeautifulSoup(book.content,"html.parser") #解析
taglist = sp3.find_all('a',{"class":" tag"}) #找标签信息
tag = ""
lis = []
for tagurl in taglist:
sp4 = BeautifulSoup(str(tagurl),"html.parser") #解析每个标签
lis.append(str(sp4.a.string)) tag = ','.join(lis) #加逗号
the_img = "I:\\douban\\image\\"+bookName+".jpg"
writelist=[i+j,bookName,nickname,rating,nums,the_img,bookurl,notion,tag]
for k in range(0,9):
if k == 5:
worksheet.insert_image(i+j,k,the_img)
else:
worksheet.write(i+j,k,writelist[k])
txtfile.write(str(writelist[k]))
txtfile.write('\t')
txtfile.write(u'\r\n') def main():
now = datetime.now() #开始计时
print(now) txtfile = codecs.open("top2501.txt",'w','utf-8')
url = "http://book.douban.com/top250?" header = { "User-Agent": "Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.13 Safari/537.36",
"Referer": "http://book.douban.com/"
} image_dir = "I:\\douban\\image\\"
#建立Excel
workbookx = xlsxwriter.Workbook('I:\\douban\\booktop250.xlsx')
worksheet = workbookx.add_worksheet()
format = workbookx.add_format()
#format.set_align('justify')
format.set_align('center')
#format.set_align('vjustify')
format.set_align('vcenter')
format.set_text_wrap() worksheet.set_row(0,12,format)
for i in range(1,251):
worksheet.set_row(i,70)
worksheet.set_column('A:A',3,format)
worksheet.set_column('B:C',17,format)
worksheet.set_column('D:D',4,format)
worksheet.set_column('E:E',7,format)
worksheet.set_column('F:F',10,format)
worksheet.set_column('G:G',19,format)
worksheet.set_column('H:I',40,format) item = ['书名','别称','评分','评价人数','封面','图书链接','出版信息','标签']
for i in range(1,9):
worksheet.write(0,i,item[i-1]) s = requests.Session() #建立会话
s.get(url,headers=header) thread = [] for i in range(0,250,25):
geturl = url + "/start=" + str(i) #要获取的页面地址
print("Now to get " + geturl)
t = threading.Thread(target=crawler,
args=(s,i,url,header,image_dir,worksheet,txtfile))
thread.append(t) for i in range(0,10):
thread[i].start() for i in range(0,10):
thread[i].join() end = datetime.now() #结束计时
print(end)
print("程序耗时: " + str(end-now))
txtfile.close()
workbookx.close() if __name__ == '__main__':
main()
虽然还是写的有点乱。。 然后运行:
2016-03-29 08:48:37.006681
Now to get http://book.douban.com/top250?/start=0
Now to get http://book.douban.com/top250?/start=25
Now to get http://book.douban.com/top250?/start=50
Now to get http://book.douban.com/top250?/start=75
Now to get http://book.douban.com/top250?/start=100
Now to get http://book.douban.com/top250?/start=125
Now to get http://book.douban.com/top250?/start=150
Now to get http://book.douban.com/top250?/start=175
Now to get http://book.douban.com/top250?/start=200
Now to get http://book.douban.com/top250?/start=225
2016-03-29 08:49:44.003378
程序耗时: 0:01:06.996697
只花费了1分6秒,与前面的7分24秒相比,加速比达到6.7!这就是多线程的优势,理论上应该达到将近10倍的,但是由于线程创建和切换也是有开销的,所以达到7~8倍就不错了。然后我又运行了几次,稳定性还行,没有崩过。 ps:这个博客模板默认换行怎么辣么多,代码里面都自动换行。。
(完)
【Python数据分析】Python3多线程并发网络爬虫-以豆瓣图书Top250为例的更多相关文章
- 【Python数据分析】Python3操作Excel-以豆瓣图书Top250为例
本文利用Python3爬虫抓取豆瓣图书Top250,并利用xlwt模块将其存储至excel文件,图片下载到相应目录.旨在进行更多的爬虫实践练习以及模块学习. 工具 1.Python 3.5 2.Bea ...
- #1 爬虫:豆瓣图书TOP250 「requests、BeautifulSoup」
一.项目背景 随着时代的发展,国人对于阅读的需求也是日益增长,既然要阅读,就要读好书,什么是好书呢?本项目选择以豆瓣图书网站为对象,统计其排行榜的前250本书籍. 二.项目介绍 本项目使用Python ...
- Python 2.7_利用xpath语法爬取豆瓣图书top250信息_20170129
大年初二,忙完家里一些事,顺带有人交流爬取豆瓣图书top250 1.构造urls列表 urls=['https://book.douban.com/top250?start={}'.format(st ...
- Python爬虫-爬取豆瓣图书Top250
豆瓣网站很人性化,对于新手爬虫比较友好,没有如果调低爬取频率,不用担心会被封 IP.但也不要太频繁爬取. 涉及知识点:requests.html.xpath.csv 一.准备工作 需要安装reques ...
- 使用Python写的第一个网络爬虫程序
今天尝试使用python写一个网络爬虫代码,主要是想訪问某个站点,从中选取感兴趣的信息,并将信息依照一定的格式保存早Excel中. 此代码中主要使用到了python的以下几个功能,因为对python不 ...
- 吴裕雄--天生自然python学习笔记:编写网络爬虫代码获取指定网站的图片
我们经常会在网上搜索井下载图片,然而一张一张地下载就太麻烦了,本案例 就是通过网络爬虫技术, 一次性下载该网站所有的图片并保存 . 网站图片下载并保存 将指定网站的 .jpg 和 .png 格式的图片 ...
- 【Python爬虫】:使用高性能异步多进程爬虫获取豆瓣电影Top250
在本篇博文当中,将会教会大家如何使用高性能爬虫,快速爬取并解析页面当中的信息.一般情况下,如果我们请求网页的次数太多,每次都要发出一次请求,进行串行执行的话,那么请求将会占用我们大量的时间,这样得不偿 ...
- python系列之(3)爬取豆瓣图书数据
上次介绍了beautifulsoup的使用,那就来进行运用下吧.本篇将主要介绍通过爬取豆瓣图书的信息,存储到sqlite数据库进行分析. 1.sqlite SQLite是一个进程内的库,实现了自给自足 ...
- 爬虫实战 豆瓣音乐top250 xpath
刷知乎时刷到一篇爬取豆瓣音乐top250的,然后看了看,感觉自己的爬虫又更上一层楼了哈啊哈哈,尤其是发现xpath这么好用的东西. 不过也有一个感慨,就是有很多种方式都可以获得想要的数据,对于入门的新 ...
随机推荐
- ADO.net数据绑定
using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; usin ...
- EC笔记,第二部分:5.了解C++默默编写并调用哪些函数
5.了解C++默默编写并调用哪些函数 1.C++空类 C++会为一个空类建立以下函数 (1).默认构造函数 (2).默认拷贝构造函数 (3).析构函数 (4).赋值运算符(如果成员包含引用类型或con ...
- Java程序员必须掌握的8大排序算法
分类: 1)插入排序(直接插入排序.希尔排序)2)交换排序(冒泡排序.快速排序)3)选择排序(直接选择排序.堆排序)4)归并排序5)分配排序(基数排序) 所需辅助空间最多:归并排序所需辅助空间最少:堆 ...
- 线程池深入(li)
java线程池.在jdk5之后为我们提供了线程池,只需要使用API,不用去考虑线程池里特殊的处理机制.jdk5线程池分好多种,固定尺寸的线程池.可变尺寸连接池等.常用的是ThreadPoolExecu ...
- php实现设计模式之 简单工厂模式
作为对象的创建模式,用工厂方法代替new操作. 简单工厂模式是属于创建型模式,又叫做静态工厂方法模式,但不属于23种GOF设计模式之一.简单工厂模式是由一个工厂对象决定创建出哪一种产品类的实例. 工厂 ...
- socket.io,命名空间
原文:http://www.cnblogs.com/xiezhengcai/p/3966263.html 命名空间 在api部分我们说io.connect('ws://103.31.201.154:5 ...
- GJM : C#语言学习笔记
--------------------------------------C#--------------------------------------if (tom == null) tom = ...
- 实现UITextView的placeholder
我们知道在iOS开发时,控件UITextField有个placeholder属性,UITextField和UITextView使用方法基本类似,有两个小区别:1.UITextField单行输入,而UI ...
- ArcGIS安装错误1402
以前出遇到过在安装ArcGIS软件的时候出现1402错误,错误原因是因为权限不足,具体是什么造成权限不足还不清楚,估计是安装了什么软件后造成. 错误信息 解决方法 找到相应的注册表,在项上右键设置权限 ...
- 初识JavaScript 变量, 操作符, 数组
这里讲的不会太多, 因为所有的语言都是一样的, 一些基本的东西, 所以就随便写写. 变量 变量就是可变的量, 编程角度理解就是用于存储某种/某些数值的存储器. 我们可以把变量具象理解为一个盒子, 而我 ...