python多线程小爬虫之练练手

序:

叮咚叮咚,走过路过不要错过啊。。。

好久没写博客,上次都是几年前了,写过一些android的文章,然而我连账号都忘记了,真尼玛。。。

大数据时代,人工智能,机器学习开始流行了,赶上了这个时代是好的,也是不那么好的,好的是将会为社会带来革命性的洗礼,不那么好的是感觉出生还是太早了点哈哈哈,要是晚几十年生,等我风华正茂的时候,那是多壮观的世界,yy中.....


正文:

好了 ,上个线程还在沉浸在yy中,作为一个多任务处理的灵长类,必须开个线程要说点正事了。

大家都知道机器学习是需要大量的数据来训练模型,通过大量训练模型才能自我进化,然而我们不是在BAT这样的巨头的话,是很难获得巨额数据的,那只能干个苦力活自己去网上找咯,身为程序员还是要让代码来做吧,爬虫就很适合啦,刚好python就是爬虫的利器,之前学习的时候写了个爬百度贴吧评论的小爬虫,贴出来先献丑啦(吸取硬盘挂了的教训,写过的轮子交给csdn帮我保存吧  /大哭/大哭)

用到的知识:

python多线程

xpath

python的多线程是一一映射到系统层的,也就是说,每开一个python的线程,其内部也会调用系统接口去开启一个对应的系统级别的C程序线程,真正干活的就是这些对应c线程,但是这些c线程是系统级的,使用python的我们在代码级层面是无法控制它们的,失去控制的线程时可怕的,说白了就会乱搞数据,所以就有了著名的GIL,它帮助我们控制这些c线程。

学习python的都知道,python的GIL(全局解释锁,解释器Cpython加入的一种机制)是个麻烦的家伙,由于它的限制作用,使得程序在同一个时刻只有一个C线程在工作,无法充分发挥cpu多核心多线程的能力,就相当于单核作战(本质上就相当于单核CPU通过不断的分配时间片来模拟多线程的方法),换言之就是你的工厂有8个人,但是他们都不同时工作,根据策略切换,每次切换只有一个人在干活,其他7个人傻愣着,这就恶心了,你工厂的效能就无法全部发挥出来,所以正因为这个点,python无法真正做到并行,只是并发(伪并行,一起启动,但不能一起执行),但是我们要注意的是GIL只是python解释器内核级的锁,解释器上层还有用户层,用户层面python也提供了lock类,因为GIL的存在只是相当于把多核变成了单核,但无论多核还是单核,对于操作共有资源,并发操作,存在资源竞争的话就几乎会有锁的应用,不然数据混乱往往就会发生在线程切换时,每个线程看到的资源都会不一样了,所以在单核时代多线程也是会加锁的,举个例子:

import threading,time,random
sum = 0
class Sum(threading.Thread):
    def run(self):
        global sum
        for i in range(2):
            time.sleep(random.randint(50,100)/100.0)
	   # 仔细对比如果不加锁,有时候会最终结果出现199,甚至198,要有耐心,不容易出现,
	   # 并发运行中包含资源竞争就一定会用锁,我们可以做的是将锁的粒度降到最小 并且找出原子操作...
           # lock.acquire() 
            sum += 1
          #  ock.release()  python3测试了下不需要再我们代码加锁了,貌似对解释器做了优化,我猜测是在target指向的函数外层默认加了把锁。
            print sum
for i in range(100):
    s = Sum()
    s.start()

        在前端页面中很多标签,在javaWeb中有三种方法解析xml里的标签,分别是Dom解析,sex解析和pull解析,Dom解析是把整个文档树封装成对象载入内存,再去获取你想要的节点,这样使得无论你处于哪个节点你都能获得上下文,而sex解析是逐行解析的,所以不知道当前节点的前后关系。。。哎妈呀,扯远了。。

xpath是一种技术,是一种语言规则,通过使用这种语言规则可以使得在解析页面的时候可以类似js那样通过Dom对象来获取前端页面中的各个节点(注意,只是个人感觉大概类似,用法是不一样的),以此来获取节点中的数据,xpath就封装在lxml的模块中,可以通过pip install lxml来导入模块,有些同学可以会说BeautifulSoup也很不错,确实就用户友好性而言,BeautifulSoup甩了xpath几条街。。。,但是就性能而言xpath是用c语言实现的,BeautifulSoup是用python实现的,xpath在速度上是有优势的,你想想还有除了汇编还有啥比c快的。。。,而且BeautifulSoup是基于Dom解析的,是把整个文档数加载进内存的,比较占用内存资源,查找起来也比较就耗时了,但是就使用友好性来说,BeautifulSoup总算是扳回一局了,举个例子:

title = soup.select('.title.content div .cc')  #BeautifulSoup支持css选择器,够人性啊

title = selector.xpath("//div[@class='content']/div[@class='content']/")  #这xpath看起来就感觉复杂很多了,不过我觉得本质上也是逐层往下选择的。。。

综合各方面,只要用多了我觉得xpath还是很有可取之处的,偏向于xpath吧,至于最后的正则表达式,恩!!!此时脑海中飘过一堆(.*?),.*,我不想说。。 好难记啊,基于理解去记忆都难,但是该用的时候还得用不是么....一万点悲伤

前面说完了要用的东西也说了下自己掌握的知识,现在就要用上了,贴代码。。

#coding=utf8
from lxml import html
from lxml import etree
import requests
import json
from multiprocessing.dummy import Pool as ThreadPool
import sys
reload(sys)  #模块加载时sys.setdefaultencoding被通过反射删除了,要使用需重新加载
sys.setdefaultencoding('utf-8')

def write_to_txt(comment):
    f.writelines('评论用户:' + comment['user_name'] + '\n')
    f.writelines('用户ID:' + str(comment['user_id']) + '\n') #注意json中有恶心的数据。。。
    f.writelines('评论内容:' + comment['comment'] + '\n')
    f.writelines('评论时间:' + comment['date'] + '\n')
    f.writelines('\n\n')


def get_content(url):
    # 这里用的requests的模块,在https下是有问题的,我这里是http,看不出来,客官可自行调试https会报openssl的错,在centos下有些可以有些不可以,windows下100%错
    # 因为我windows没装openssl证书哈哈哈,还要下载个vs编译,太大了,我就换种方式了。。。
    # 在python3上requests就应该没问题了,因为人家提示你升级啦,python2的使用urllib2吧,我试了https没问题
    data = requests.get(url)
    selector = etree.HTML(data.text)
    #//表示从根节点开始,关于xpath的语法大家要自行去预习啦
    comment_list = selector.xpath('//div[@class="l_post j_l_post l_post_bright  "]')
    item = {}
    for each in comment_list:
        try:
            json_obj = json.loads(each.xpath('@data-field')[0])
            item['user_name'] = json_obj['author']['user_name']
            item['user_id'] = json_obj['author']['user_id']
            item['date'] = json_obj['content']['date']
            #这里的这么一大串无非就是逐层标签往下找,class的就在前段页面copy,因为不是从根节点开始找了,所以无需//开头
            comment = each.xpath('div[@class="d_post_content_main"]/div/cc/div[@class="d_post_content j_d_post_content  clearfix"]/text()')[0]
            item['comment'] = comment
            #因为没有做同步,最后爬出来的数据仔细看时间会发现是乱序的,but没关系,我们只是看数据内容,顺序无妨
            write_to_txt(item)
        except Exception as e:
            continue
    return

if __name__ == '__main__':
    pool = ThreadPool(4)
    f = open('teiba_cpmment', 'w')
    url_list = []
    for i in range(1, 21):
	#经过分析,某帖子的翻页只是参数的变化,那就比较简单了
        url = "http://tieba.baidu.com/p/3522395718?pn=%s" %i  
        url_list.append(url)
   #这里使用了multiprocessing.dummy里面的一个线程池,它还有个进程池,还有一种常规方式就是使用for循环,某天发现居然还能通过map函数来实现并发,一句话的事啊
   #略叼略叼,for循环方式好好在可能拿到线程的集合,方便对线程做存活判断,下一篇我再写个for循环的,域名逐层爬链接,配合下队列实现
    pool.map(get_content, url_list)
    pool.close()
    pool.join()
    f.close()


爬出来的的结果:
值得注意的是这个效果是分析url和前端页面标签后配合xpath得出的,如果小伙伴们换了url要重新分析xpath的筛选字符哦,比如抓个https的试试啦啦啦

python多线程小爬虫之练练手_第1张图片

大概就是爬出来这些,20页除去报错的也有差不几百条了,一个用户算一条吧,展示的图片数据我做了id和和用户名更改的,虽然去网页上能直接看到,但是还是
不要这么直接啦,小小保护哈哈哈。。。每次写文章都写很久。。。五一快乐,洗个澡睡觉觉。

 
  
 
 

你可能感兴趣的:(python之爬虫系列)