【python数据抓取技术与实战】单机数据抓取

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

可以发现相比单线程而言,多线程执行方式的确能够提高抓取的效率。然而多线程的在网络良好的情况是远远比不上多进程的,因为多进程抓取的快慢主要取决于网络。协程,又叫做子例程,可以把它看作是一种轻量的线程,似乎比多线程的处理更高效。

猜你喜欢

转载自blog.csdn.net/dylan_me/article/details/80931190
今日推荐