1、单机顺序抓取
这里使用的是Spynner库进行单进程抓取,通常用于目标明确及抓取内容数量不是很大的情况。加之我们用的python3,安装过程也复杂,所以我们就不必在这部分花时间了。但在这里,我们做一个简单的介绍。
优点:Spynner基于pyqt库,pyqt封装了强大的webkit,具有执行JavaScript的能力,可以完全模拟一个浏览器的功能和行为。
import spynner #引入模块
browser=spynner.Browser() #生成一个“浏览器”
browser.show() #显示“浏览器”
browser.load('http://www.phei.com.cn') #加载网页
browser.wait(10) #等待10秒,为了使“浏览器”多停留一会,以便观察
browser.close() #关闭“浏览器”,彻底释放资源2、requests
requests库,它是python的http库,可以完成绝大部分与http应用相关的工作。与spynner不同的是,requests是一种非界面化的库,不能模拟浏览器的全部行为,常用的两种方法为:get和post。
get方法在爬虫基础那一讲,我们已经知道了它的部分使用方法。我们来一起看一下代码:
import requests
#形式一
resp1=requests.get('http://www.phei.com.cn/')
#形式二
params_dict ={
'key1':'value1',
'key2':'value2'
}
resp2=requests.get('http://httpbin.org/get',params=params_dict)
print(resp2.json())post方法,比get方法的形式更复杂一些。get方式提交的数据最多只能是1024字节,post是没有大小限制的。
还是先来看看代码:
import requests
post_data ={
'user':'uname',
'upass':'upassword'
}
resp3=requests.post('http://httpbin.org/get',data=post_data)
print(resp3.json())header和cookie
header可以将我们的get和post伪装成浏览器,因为在抓取时对方的可能会判断请求方是否是浏览器,如果为浏览器才响应,返回数据。cookie中常常包含浏览器信息和用户信息,某些网站就是通过cookie信息来判断用户的。
3、并发和并行
主要技术包括:多线程、多进程、携程gevent。下面我们就进入正题:
为了更好的运用技术,我们先解释以下概念
- 并发,在一个时间段内发生若干事件的情况。就像单核的CPU,多任务操作系统的各个任务是并发运行的,各个任务会以分时的方式在一段时间内分别占用CPU依次执行,如果在自己的时间段里没有完成,那么需等待下一次得到CPU的使用权才会继续。
- 并行,同一时刻发生若干时间的情况。就像多核的CPU,多个任务可能在多个核上同时运行。
- 同步,并发或并行发生的各个任务之间不是孤立独自运行的,一个任务的进行可能需要在获得另一个任务给出的结果之后。各任务的运行会彼此相互制约。
- 异步,并发或并行发生的各个任务之间彼此是独立运行的,不受各自的影响。
实验爬取计算机类的图书名和链接,单线程、多线程、多进程、协程的时间比较,代码如下:
import requests
from bs4 import BeautifulSoup
import threading #线程
import multiprocessing #进程
import gevent #协程
from gevent import monkey #协程中需导入的包
monkey.patch_all() #协程没有这两句,会变成依顺序抓取
import time
def format_str(s):
return s.replace('\n','').replace(' ','').replace('\t','')
def get_urls_in_pages(from_page_num,to_page_num):
urls=[]
search_word='计算机'
url_part_1='http://www.phei.com.cn/module/goods/searchkey.jsp?Page='
url_part_2='&Page=2&searchKey='
for i in range(from_page_num,to_page_num+1):
urls.append(url_part_1+str(i)+url_part_2+search_word)
all_herf_list=[]
for url in urls:
#print(url)
resp=requests.get(url).text
bs=BeautifulSoup(resp,'lxml')
a_list=bs.find_all('a')
needed_list=[]
for a in a_list:
if 'href' in a.attrs:
href_val=a['href']
title=a.text
if 'bookid' in href_val and 'shopcar0.jsp' not in href_val and title !='':
if [title,href_val] not in needed_list:
needed_list.append([format_str(title),format_str(href_val)])
all_herf_list+=needed_list
all_herf_file=open(str(from_page_num)+'_'+str(to_page_num)+'_'+'all_hrefs.txt','w')
for href in all_herf_list:
all_herf_file.write('\t'.join(href)+'\n')
all_herf_file.close()
#print(from_page_num,to_page_num,len(all_herf_list))
def single_threads_test():
t1=time.time()
get_urls_in_pages(1,40)
t2=time.time()
print('单线程使用时间:',t2-t1)
def multiple_threads_test():
t1=time.time()
page_range_lst=[(1,10),(11,20),(21,30),(31,40)]
th_lst=[]
for page_range in page_range_lst:
th=threading.Thread(target=get_urls_in_pages,args=(page_range[0],page_range[1]))
th_lst.append(th)
for th in th_lst:
th.start()
for th in th_lst:
th.join()
t2=time.time()
print('多线程使用时间:',t2-t1)
def multiple_process_test():
t1=time.time()
page_range_lst=[(1,10),(11,20),(21,30),(31,40)]
pool=multiprocessing.Pool(processes=4)
for page_range in page_range_lst:
pool.apply_async(get_urls_in_pages,page_range[0],page_range[1])
pool.close()
pool.join()
t2=time.time()
print('多进程使用时间:', t2 - t1)
def gevent_test():
t1=time.time()
page_range_lst = [(1, 10), (11, 20), (21, 30), (31, 40)]
jobs=[]
for page_range in page_range_lst:
jobs.append(gevent.spawn(get_urls_in_pages,page_range[0],page_range[1]))
gevent.joinall(jobs)
t2=time.time()
print('协程使用时间:', t2 - t1)
if __name__=='__main__':
single_threads_test()
multiple_threads_test()
multiple_process_test()
gevent_test()
输出结果如下:
单线程使用时间: 280.38603711128235 多线程使用时间: 68.45191502571106 多进程使用时间: 1.3390767574310303 协程使用时间: 15.374879360198975
可以发现相比单线程而言,多线程执行方式的确能够提高抓取的效率。然而多线程的在网络良好的情况是远远比不上多进程的,因为多进程抓取的快慢主要取决于网络。协程,又叫做子例程,可以把它看作是一种轻量的线程,似乎比多线程的处理更高效。
3046



被折叠的 条评论
为什么被折叠?



