目标html
1、网址 :百度搜 - 小米应用商店,进入官网 2、目标 :全部应用分类 应用名称 应用连接
实现步骤python
1、页面局部刷新 2、右键查看网页源代码,搜索关键字未搜到 # 此网站为动态加载网站,须要抓取网络数据包分析
1、抓取返回json数据的URL地址(Headers中的Request URL) http://app.mi.com/categotyAllListApi?page={}&categoryId=2&pageSize=30 2、查看并分析查询参数(headers中的Query String Parameters) page: 1 categoryId: 2 pageSize: 30 # 只有page在变,0 1 2 3 ... ... ,这样咱们就能够经过控制page的值拼接多个返回json数据的URL地址
# 注意多线程写入的线程锁问题 from threading import Lock lock = Lock() # 加锁 lock.acquire() python语句 # 释放锁 lock.release()
1、在 __init__(self) 中建立文件对象,多线程操做此对象进行文件写入 self.f = open('xiaomi.csv','a',newline='') self.writer = csv.writer(self.f) self.lock = Lock() 2、每一个线程抓取1页数据后将数据进行文件写入,写入文件时须要加锁 def parse_html(self): app_list = [] for xxx in xxx: app_list.append([name,link,typ]) self.lock.acquire() self.wirter.writerows(app_list) self.lock.release() 3、全部数据抓取完成关闭文件 def main(self): self.f.close()
import requests from threading import Thread from queue import Queue import time from useragents import ua_list from lxml import etree import csv from threading import Lock import random class XiaomiSpider(object): def __init__(self): self.url = 'http://app.mi.com/categotyAllListApi?page={}&categoryId={}&pageSize=30' # 存放全部URL地址的队列 self.q = Queue() self.i = 0 # 存放全部类型id的空列表 self.id_list = [] # 打开文件 self.f = open('xiaomi.csv','a') self.writer = csv.writer(self.f) # 建立锁 self.lock = Lock() def get_cateid(self): # 请求 url = 'http://app.mi.com/' headers = { 'User-Agent': random.choice(ua_list)} html = requests.get(url=url,headers=headers).text # 解析 parse_html = etree.HTML(html) xpath_bds = '//ul[@class="category-list"]/li' li_list = parse_html.xpath(xpath_bds) for li in li_list: typ_name = li.xpath('./a/text()')[0] typ_id = li.xpath('./a/@href')[0].split('/')[-1] # 计算每一个类型的页数 pages = self.get_pages(typ_id) self.id_list.append( (typ_id,pages) ) # 入队列 self.url_in() # 获取counts的值并计算页数 def get_pages(self,typ_id): # 每页返回的json数据中,都有count这个key url = self.url.format(0,typ_id) html = requests.get( url=url, headers={'User-Agent':random.choice(ua_list)} ).json() count = html['count'] pages = int(count) // 30 + 1 return pages # url入队列 def url_in(self): for id in self.id_list: # id为元组,('2',pages) for page in range(1,id[1]+1): url = self.url.format(page,id[0]) # 把URL地址入队列 self.q.put(url) # 线程事件函数: get() - 请求 - 解析 - 处理数据 def get_data(self): while True: if not self.q.empty(): url = self.q.get() headers = {'User-Agent':random.choice(ua_list)} html = requests.get(url=url,headers=headers).json() self.parse_html(html) else: break # 解析函数 def parse_html(self,html): # 存放1页的数据 - 写入到csv文件 app_list = [] for app in html['data']: # 应用名称 + 连接 + 分类 name = app['displayName'] link = 'http://app.mi.com/details?id=' + app['packageName'] typ_name = app['level1CategoryName'] # 把每一条数据放到app_list中,目的为了 writerows() app_list.append([name,typ_name,link]) print(name,typ_name) self.i += 1 # 开始写入1页数据 - app_list self.lock.acquire() self.writer.writerows(app_list) self.lock.release() # 主函数 def main(self): # URL入队列 self.get_cateid() t_list = [] # 建立多个线程 for i in range(1): t = Thread(target=self.get_data) t_list.append(t) t.start() # 回收线程 for t in t_list: t.join() # 关闭文件 self.f.close() print('数量:',self.i) if __name__ == '__main__': start = time.time() spider = XiaomiSpider() spider.main() end = time.time() print('执行时间:%.2f' % (end-start))