1200字范文,内容丰富有趣,写作的好帮手!
1200字范文 > 爬虫第十式:多线程爬取小米应用商店聊天社交类别

爬虫第十式:多线程爬取小米应用商店聊天社交类别

时间:2018-09-08 22:20:35

相关推荐

爬虫第十式:多线程爬取小米应用商店聊天社交类别

温馨提示:

爬虫玩得好,监狱进得早。数据玩得溜,牢饭吃个够。

《刑法》第 285 条,非法获取计算机信息系统数据罪。

违反国家规定,侵入前款规定以外的计算机信息系统或者采用其他技术手段,获取该计算机信息系统中存储、处理或者传输的数据,或者对该计算机信息系统实施非法控制,情节严重的,处三年以下有期徒刑或者拘役,并处或者单处罚金;情节特别严重的,处三年以上七年以下有期徒刑,并处罚金。

正文:

本章我们来介绍一个新的方式爬虫实用知识,多线程,可能有人会问为什么不用多线程,这个其实说实话,多线程用的会多一点,进程用的少,多线程为主

应用场景

【1】多进程:CPU密集程序【2】多线程:爬虫(网络I/O)、本地磁盘I/O

创建队列,等待线程进入

【1】导入模块 from queue import Queue 【2】使用 q = Queue()q.put(url) q.get() # 当队列为空时,阻塞 q.empty() # 判断队列是否为空, True/False【3】q.get()解除阻塞方式 3.1) q.get(block=False) 3.2) q.get(block=True,timeout=3) 3.3) if not q.empty(): q.get()

创建线程模块

# 导入模块from threading import Thread# 使用流程 t = Thread(target=函数名) # 创建线程对象 t.start() # 创建并启动线程 t.join() # 阻塞等待回收线程# 如何创建多线程 t_list = []for i in range(5):t = Thread(target=函数名) t_list.append(t) t.start()for t in t_list: t.join()

现在我们分析一下小米应用商店的数据,先来打开看一下:

这个网站是一个动态加载的网站,我们上一个案例豆瓣电影排行榜数据抓取

提到过,这样网站的特点以及怎么抓取数据,这里我们直接进行抓取,没必要观察URL地址的规律,因为响应内容不存在,我们直接F12就抓包就好了

我们先来刷新一下,然后点击下一页,多点两下,抓取几个包就够我们用了

这个就是我们抓取的异步的网络数据包,我们从表面就能看到,问好后面的page=有0、1、2、3… 这个就是一个查询参数,那我们点开一个看看:

这个应该是,这是count有两千个应该是社交app的个数,下面我们也看到了各个应用app的名字,接下来我们换到Headers里面分析各个信息:

这里面我们分析到这个是一个GET请求,那是这样的请求的话我们就直接找URL、headers、Query String Parameters这三个东西,现在URL就是我们找到的框框里面的URL,headers就是Request Headers的信息,最后我们看一下Query String Parameters这里面的东西

这三个,看着也没有加密的东西,也没有时间戳的字符串

page应该是页数

categoryld常规来说这个是类别应用的id

pageSize这个是每页的应用个数

那看一下接下里的数据包

我们看到就是page变了,其他的都是一样的,接下来我们复制抓到的URL地址到浏览器看一下:

那我们就找到了动态加载的数据了,那我们就写写代码吧,URL的话就只是把page后面直接改成花括号就行

导入模块

import requests import jsonfrom threading import Thread, Lock # 线程模块和线程锁 from queue import Queue # 创建队列import timefrom fake_useragent import UserAgent

定义功能函数,减少重复代码

class XiaomiSpider:def __init__(self):self.url = '/categotyAllListApi?page={}&categoryId=2&pageSize=30'# 队列 锁self.q = Queue()self.lock = Lock()

再来一个函数,就是把生成的URL地址,入到队列里面,等待抓取

def url_to_queue(self):# 生成所有待抓取的URL地址for page in range(67):page_url = self.url.format(page)# 入队列self.q.put(page_url)

接下来就是创建线程事件函数从队列获取地址,请求,解析,数据处理,那我们思维谨慎一点,先来个判断,看看队列里面有没有数据,不是空的就拿出来

if not self.q.empty():# 获取url = self.q.get()

下面是取出数据后就是开始请求,解析,数据处理

html = self.get_html(url=url)html = json.loads(html)for one_app_dict in html['data']:item = {}item['name'] = one_app_dict['displayName']item['type'] = one_app_dict['level1CategoryName']item['link'] = one_app_dict['packageName']print(item)

创建run函数,把程序运行起来

def run(self):# URL地址入队列self.url_to_queue()# 创建多线程t_list = []# 线程个数for i in range(1):t = Thread(target=self.parse_html)t_list.append(t)t.start()for t in t_list:t.join()

这样代码就大体写完了,但是还是有点小问题,我们这个直接t.start开始后就直接到线程事件函数里面,这里面来之后,我们直接一个线程过来了,判断是不是空的就只是get出一个地址,然后在发请求,解析,数据处理后面没有代码直接结束了

也就是说我就获取一个地址让一个线程,进行抓取,这样的话是不对的吧,因为假如我们有几万页的地址,我们不能创建一万个线程吧,所以我们应该让一个线程抓完之后再继续抓其他的,不是停下来不干活了,所以我们直接给他一个while循环

while True:if not self.q.empty():url = self.q.get()html = self.get_html(url=url)html = json.loads(html)for one_app_dict in html['data']:item = {}item['name'] = one_app_dict['displayName']item['type'] = one_app_dict['level1CategoryName']item['link'] = one_app_dict['packageName']print(item)else:break

这样我们的多线程就是实现了,但是还有一个东西就是我们还是那面创建的线程锁没有用,现在我们也用上它,但是我得考虑一下在哪里加锁,那就看看我们在哪里操作全局变量啊,是不是在这个线程事件函数这里操作啊,是不是在这个上面操作的,或者说几乎同时操作的,

在这里举个例子:

self.q: ['http://page88.html']Thread-1: if条件成立,进入分支语句Thread-2: if条件也成立,也进入分支语句

像这样,就是当有最后一个地址的时候,线程1 if 判断,里面有,不是空的,直接进入分支语句,马上就要get获取,就在这时候,线程2 也是 if 判断了,也是判断到里面不是空的,线程2也就是进入分支,最后线程1和线程2都会执行get()语句,但是有一个会发生阻塞,那我们怎么不然它阻塞,只能是加锁控制

while True:self.lock.acquire()if not self.q.empty():url = self.q.get()print(url)# 释放锁self.lock.release()html = self.get_html(url=url)html = json.loads(html)for one_app_dict in html['data']:item = {}item['name'] = one_app_dict['displayName']item['type'] = one_app_dict['level1CategoryName']item['link'] = one_app_dict['packageName']print(item)else:# 释放锁self.lock.release()break

这样的加锁和释放锁的目的是

加锁一定对应着释放锁,如果进入到if里面了,就在if里面释放锁,如果没有进入到if里面,那就一定会走else里,那就在else里释放锁

最后将全部代码奉上:

import requestsimport jsonfrom threading import Thread, Lockfrom queue import Queueimport timefrom fake_useragent import UserAgentclass XiaomiSpider:def __init__(self):self.url = '/categotyAllListApi?page={}&categoryId=2&pageSize=30'# 队列 锁self.q = Queue()self.lock = Lock()def get_html(self, url):headers = {'User-Agent':UserAgent().random}html = requests.get(url=url,headers=headers).textreturn htmldef url_to_queue(self):for page in range(67):page_url = self.url.format(page)self.q.put(page_url)def parse_html(self):while True:self.lock.acquire()if not self.q.empty():url = self.q.get()print(url)# 释放锁self.lock.release()html = self.get_html(url=url)html = json.loads(html)for one_app_dict in html['data']:item = {}item['name'] = one_app_dict['displayName']item['type'] = one_app_dict['level1CategoryName']item['link'] = one_app_dict['packageName']print(item)else:# 释放锁self.lock.release()breakdef run(self):# URL地址入队列self.url_to_queue()# 创建多线程t_list = []for i in range(1):t = Thread(target=self.parse_html)t_list.append(t)t.start()for t in t_list:t.join()if __name__ == '__main__':start = time.time()spider = XiaomiSpider()spider.run()end = time.time()print('time:%.2f' % (end - start))

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。