Python WebbingGrap 探索一

    前端时间自学了python的基本语法,为深入了解python,就打算真正的鼓捣些东西,加深记忆。据说,python经常用来网页抓取(爬虫),故而新手小试,并记之。

网页数据采集多线程设计:

1>输入:多个线程共享一个任务队列,如果该任务方案支持网络,可设计为分布式集群采集;2>输出:最简单的做法是直接将采集数据放入数据库,但频繁操作数据库会增加耗时,故而多个输出可共享一个消息队列,再用线程处理消息队列;事实上该消息队列可模拟为OS经典生产者和消费者模式中的共享缓冲池。

1.网例一之:python多线程数据采集

要点1:python多线程

python的多线程编程,这里题外话的说一下线程和进程的区别:1>进程(有时被称为重量级进程)是程序的一次执行每个进程都有自己的地址空间,内存,数据栈以及其他记录其运行轨迹的辅助数据(os层面)。进程可通过fork和spawn操作来完成其他任务,但由于各自内存独立,故只能使用进程间通讯(IPC),而不能直接共享信息;2>线程(有时被称为轻量级进程)与进程相似,不同是所有的线程运行在同一个进程中,共享相同的运行环境(包括数据空间,故可方便通讯)。线程有开始、顺序执行和结束三部分,持有自己的指令指针,记录自己运行到什么地方。线程的运行可能被中断(抢占)或睡眠(暂时被挂起),以让步于其他线程。线程一般都是并发执行的,事实上在单CPU系统中真正的并发是不可能的,每个线程会被安排成每次只占用CPU运行一段时间。多个线程共同访问同一片数据有可能导致数据结果的不一致的问题,这叫做竞态条件。

至于Python的多线程,由于其原始解释器CPython中存在着GIL(Global Interpreter Lock,全局解释器锁),虽然CPython线程库直接封装了系统的原生线程,但(整体进程)同一时间只会获得一个GIL线程执行,直到解释器遇到I/O操作或者操作次数达到一定数目时才会释放GIL。故即使多线程也只是做分时切换而已。

所以如果你的Python代码是CPU密集型,此时多线程的代码很有可能是线性执行的,这种情况下多线程是鸡肋,效率可能还不如单线程因;但如果你的代码是IO密集型,Python多线程可以明显提高效率。

但如果确实需要在CPU密集型的代码里用并行,可以用multiprocessing库。这个库是基于multi process实现了类multi thread的API接口,并且用pickle部分地实现了变量共享。

如果确实理不清Python代码是CPU密集型还是IO密集型,那么分别尝试下面的两种Python多线程方法:

1>from multiprocessing import Pool(这就是基于multithread实现了multiprocessing的API,多进程同步);

2>from multiprocessing.dummy import Pool(这是多线程实现的同步)两种方式都跑一下,哪个速度快用哪个就行了。

一般使用:

threading模块中的Thread类可用来创建线程,创建方法有如下两种:1.通过继承Thread类,重写它的run方法;2.创建一个threading.Thread对象,在初始化函数(__init__)中将可调用对象作为参数传入:threading.Thread.__init__(self,name=threadname)。

要点2. 目标数据及相关第三方packages

采集目标:淘宝; 采集数据:某一关键词领域的淘宝店铺名称、URL地址、店铺等级; 

相关第三方packages:requests(http://devcharm.com/pages/11-python-modules-you-should-know);beautifulsoup4(PS:保证lxml model已安装过);Redis

代码部分:

1. search_config.py


#coding=utf-8

class config:

keyword ='青岛'    search_type ='shop'   url='http://s.taobao.com/search?q='+ keyword  +'&commend=all&search_type='+ search_type +'&sourceId=tb.index&initiative_id=tbindexz_20131207&app=shopsearch&s='

simple_scrapy.py

#coding=utf-8

import request

from bs4 import BeautifulSoup

from search_config import config

from Queue import Queue

import threading

class Scrapy(threading.Thread)

def __init__(self,threadname,queue,out_queue):

    threading.Thread.__init__(self,threadname)

    self.sharedata=queue

    self.out_queue=out_queue

    self.threadname=threadname

    print threadname+'start......'

def run(self):

    url=config.url+self.sharesata.get()

    response=requests.get(url)

    self.out_queue.put(response)

    print self.threadname+'end......'

class Parse(threading.Thread):

def __init__(self,threadname,queue,out_queue):

threading.Thread.__int__(self,name=threadname)

self.sharedata=queue

self.out_queue=out_queue

self.threadname=threadname

print threadname+'start......'

def run(self)

response=self.sharedata.get()

body=response.content

soup=BeautifulSoup(body)

ul_html=soup.find('ul',{'id':'list-container'})

lists=ul_html.findAll('li',{'class':'list-item'})

stores=[]

for list in lists

store={}

try:

info=list.findAll('a',{'trace':'shop'})

 for info in infos

    attrs=dict(info.attrs)

    if attrs.has_key('class'):

       if 'rank' in attrs['class']

           rank_string=attrs['class']

          rank_num=rank_string[-2:]

          if rank_num[0]=='-':

            store['rank']=rank_num[-1]

          else:

            store['rank']=rank_num

       if attrs.has_key('title')

          store['title']=attrs['title']

          store['href']=attrs['href']

    except  AttibuteError:

      pass

if store:

   stores.append(store)

for store in stores: 

    print store['title'] +''+ store['rank']

print self.threadname +'end......'

def  main():

    queue=Queue()

    targets=Queue()

    stores=Queue()

    scrapy=[]

    for i  in range(0,13,6):#queue 原始请求#targets 等待解析的内容#stores解析完成的内容,这里为了简单直观,直接在线程中输出了内容,并没有使用该队列

        queue.put(str(i))

        scrapy= Scrapy('scrapy', queue, targets)

        scrapy.start()

        parse= Parse('parse', targets, stores)

        parse.start()

if__name__=='__main__':

   main()

注解:1》使用BeautifulSoup库,可以直接按照class或者id等html的attr来进行提取,相较于直接写正则表达式难度系数降低很多,当然执行效率上,相应的也就大打折扣了。

2》通过scrapy线程不断提供数据,parse线程从队列中取出数据进行相应解析;作者将这二者写到了同一个循环体中:即get一个response后随即刻parse掉,但我认为这二者完全可以参照生产者消费者模式,将二者模块分开,仅共享一消息队列即可。

3》由于共享数据不存在安全问题,所以上面的例子都是非线程安全的,并没有为共享数据加锁,只是实现了最简单的FIFO,所以也不会是因为锁的开销导致效率没有得到真正提高

4》由于数据解析是CPU密集型操作,而网络请求是I/O密集型操作,考虑上文说到的GIL,数据解析操作操作的多线程并不能带来效率上的提升,相反可能因为线程的频繁切换,导致效率下降;而网络抓取的多线程则可以利用IO阻塞等待时的空闲时间执行其他线程,提升效率。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 220,976评论 6 513
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,249评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 167,449评论 0 360
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,433评论 1 296
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,460评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,132评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,721评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,641评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,180评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,267评论 3 339
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,408评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,076评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,767评论 3 332
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,255评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,386评论 1 271
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,764评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,413评论 2 358

推荐阅读更多精彩内容

  • 我们前面提到了进程是由若干线程组成的,一个进程至少有一个线程。多线程优点: 在一个进程中的多线程和主线程分享相同的...
    第八共同体阅读 520评论 0 0
  • 1.进程和线程 队列:1、进程之间的通信: q = multiprocessing.Queue()2、...
    一只写程序的猿阅读 1,111评论 0 17
  • Python的面向对象 类 Class 类变量 Class variable 数据成员 Data member 函...
    JasonJe阅读 1,127评论 0 3
  • 线程 1.同步概念 1.多线程开发可能遇到的问题 同步不是一起的意思,是协同步调 假设两个线程t1和t2都要对nu...
    TENG书阅读 611评论 0 1
  • 线程 引言&动机 考虑一下这个场景,我们有10000条数据需要处理,处理每条数据需要花费1秒,但读取数据只需要0....
    不浪漫的浪漫_ea03阅读 364评论 0 0