admin管理员组

文章数量:819696

微信读书top100

要说鹅厂良心产品排行,微信读书绝对名列前茅。今天就来好好聊一聊~~~

1.页面分析

进入到。

总共有17个专栏(除男生小说榜、女生小说榜),首先看一下这17个专栏页面URL有什么规律。




……

可以看出,每个链接之间只有最后的数字有区别,可以使用列表推导式来将这些链接保存在一个列表中。

page_urls = ['/' + str(i * 100000) for i in range(1, 18)]

下面看一看每个页面内部有什么细节。

右击页面,点击检查,然后刷新一下页面,就发现了问题。

微信读书这个页面禁止了页面调试功能,可能是做了反爬虫的考虑,防止我们查看。不过解决这个问题很简单,点击一下上图中绿框中的按钮,再刷新一下页面就好了。

查看一下页面源代码,我们发现源码中只有前20本书的信息。

再下拉页面,发现达到第二十本书的时候会自动加载下面的书籍,可以断言其他的书籍内容都是动态加载得到的。于是查看一下network下的XHR,果然发现每翻20本书都会动态加载一个文件。

将响应的内容复制出来在,发现后来加载的信息果然在里面。

再来看一下这些动态加载出来的URL有什么特点。

这是一个GET请求,每次加载的链接如下:

=20
=40
……
=20
=40
……

可以得知来自同一个页面链接之间只有maxIndex=不同,依然可以通过列表推导式来将这些链接保存在一个列表中。

ajax_urls = ['/{0}?maxIndex={1}'.format(i * 100000, j) for i in range(1, 18)for j in range(20, 100, 20)]

2.页面爬取和解析

上面我们已经得到了所有要爬取的链接,就可以通过requests库来爬取数据了。由于有的信息存在于页面源代码,有的是动态加载出来的,所以打算分开来爬取,并且page_urls中的内容利用xpath解析,ajax_urls中的内容利用正则表达式来解析。

先分析利用page_urls中URL爬取的信息。这里打算获取排名、书名、作者、评分、今日阅读人数、简介6个信息。分别复制每个数据的xpath如下:

//*[@id="routerView"]/div[2]/ul/li[1]/div[1]/p
//*[@id="routerView"]/div[2]/ul/li[1]/div[1]/div[2]/p[1]
//*[@id="routerView"]/div[2]/ul/li[1]/div[1]/div[2]/p[2]/a
//*[@id="routerView"]/div[2]/ul/li[1]/div[1]/div[2]/p[3]/span[1]
//*[@id="routerView"]/div[2]/ul/li[1]/div[1]/div[2]/p[3]/span[4]/em
//*[@id="routerView"]/div[2]/ul/li[1]/div[1]/div[2]/p[4]

上面是第一本书的6个信息的xpath,我们可以从页面源代码看出每本书的内容都在同一个ul下的li标签内,所以只需要将所有的li[1]改成li,然后通过/text()获取其中的文本内容就可以获取20本书的内容了,修改后的xpath如下:

//*[@id="routerView"]/div[2]/ul/li/div[1]/p/text()
//*[@id="routerView"]/div[2]/ul/li/div[1]/div[2]/p[1]/text()
//*[@id="routerView"]/div[2]/ul/li/div[1]/div[2]/p[2]/a/text()
//*[@id="routerView"]/div[2]/ul/li/div[1]/div[2]/p[3]/span[1]/text()
//*[@id="routerView"]/div[2]/ul/li/div[1]/div[2]/p[3]/span[4]/em/text()
//*[@id="routerView"]/div[2]/ul/li/div[1]/div[2]/p[4]/text()

再分析动态加载的数据,分析结果如图:


这个很适合使用正则表达式来解析,如下:

index_path = repile('"searchIdx":(\d+)')
name_path = repile('"title":"(.*?)"')
author_path = repile('"author":"(.*?)"')
score_path = repile('"star":(\w+)')
number_path = repile('"readingCount":(\d+)')
info_path = repile('"intro":"(.*?)"', re.S)

3.数据保存到Excel

代码如下:

class save_excel:def __init__(self):self.book = xlwt.Workbook(encoding='utf-8')  # 创建workbook对象self.savepath = "微信读书Top100.xls"def writeData1(self, data):'''建立sheet表,写入data1数据'''sheet = self.book.add_sheet('微信读书top100_%s' % data[-1], cell_overwrite_ok=True)  # True为每次覆盖以前内容col = ('id', '书名', '作者', '评分', '阅读人数', '概况')for i in range(0, len(col)):  # 写列名print("写%d列" % i)sheet.write(0, i, col[i])for j in range(0, len(data[i])):sheet.write(j + 1, i, data[i][j])def writeData2(self, data):'''追加data2数据'''sheet = self.book.get_sheet('微信读书top100_%s' % data[-2])for i in range(0, 6):  # 写列名for j in range(int(data[-1]), int(data[-1]) + len(data[i])):sheet.write(j + 1, i, data[i][j - int(data[-1])])def saveData(self):'''保存到文件savepath路径'''self.book.save(self.savepath)

4.设置多线程

由于要爬取和解析的内容很多,所以设置多线程来加速程序的执行,并构造线程安全的队列Queue。代码如下:

class spider1(threading.Thread):def __init__(self, page_queue, lock, *args, **kwargs):super(spider1, self).__init__(*args, **kwargs)self.page_queue = page_queuedef run(self):if self.page_queue.empty() != True:data1 = crawl_and_parse.spider1()save_excel.writeData1(data1)else:returnclass spider2(threading.Thread):def __init__(self, ajax_queue, lock, *args, **kwargs):super(spider2, self).__init__(*args, **kwargs)self.ajax_queue = ajax_queuedef run(self):if self.ajax_queue.empty() != True:data2 = crawl_and_parse.spider2()save_excel.writeData2(data2)else:returnif __name__ == '__main__':page_queue = queue.Queue(17)ajax_queue = queue.Queue(200)lock = threading.Lock()save_excel = save_excel()crawl_and_parse = crawl_and_parse(page_queue, ajax_queue, lock)for page_url in page_urls:page_queue.put(page_url)for ajax_url in ajax_urls:ajax_queue.put(ajax_url)for i in range(20):th1 = spider1(page_queue, lock, name="爬虫1线程%d" % i)thread_list.append(th1)for j in range(200):th2 = spider2(ajax_queue, lock, name="爬虫2线程%d" % j)thread_list.append(th2)for t in thread_list:t.start()t.join()

5.全部代码

# -*- coding = utf-8 -*-
# @Time: 2021/2/16 11:10
# @File: spider.py
# @Software: PyCharmimport requests
import threading
import random
import queue
from lxml import etree
import re
import xlwt
import timepage_urls = ['/' + str(i * 100000) for i in range(1, 18)]
ajax_urls = ['/{0}?maxIndex={1}'.format(i * 100000, j) for i in range(1, 18)for j in range(20, 100, 20)]USER_AGENT = ["Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)","Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0; Trident/4.0)","Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0)","Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)","Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Maxthon 2.0)","Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; TencentTraveler 4.0)","Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)","Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; The World)","Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Avant Browser)","Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)",
]
user_agent = random.choice(USER_AGENT)
headers = {'User-Agent': user_agent
}index_path = repile('"searchIdx":(\d+)')
name_path = repile('"title":"(.*?)"')
author_path = repile('"author":"(.*?)"')
score_path = repile('"star":(\w+)')
number_path = repile('"readingCount":(\d+)')
info_path = repile('"intro":"(.*?)"', re.S)
thread_list = []class save_excel:def __init__(self):self.book = xlwt.Workbook(encoding='utf-8')  # 创建workbook对象self.savepath = "微信读书Top100.xls"def writeData1(self, data):'''建立sheet表,写入data1数据'''sheet = self.book.add_sheet('微信读书top100_%s' % data[-1], cell_overwrite_ok=True)  # True为每次覆盖以前内容col = ('id', '书名', '作者', '评分', '阅读人数', '概况')for i in range(0, len(col)):  # 写列名print("写%d列" % i)sheet.write(0, i, col[i])for j in range(0, len(data[i])):sheet.write(j + 1, i, data[i][j])def writeData2(self, data):'''追加data2数据'''sheet = self.book.get_sheet('微信读书top100_%s' % data[-2])for i in range(0, 6):  # 写列名for j in range(int(data[-1]), int(data[-1]) + len(data[i])):sheet.write(j + 1, i, data[i][j - int(data[-1])])def saveData(self):'''保存到文件savepath路径'''self.book.save(self.savepath)class crawl_and_parse:def __init__(self, page_queue, ajax_queue, lock):self.page_queue = page_queueself.ajax_queue = ajax_queueself.lock = lockdef spider1(self):'''输入:page_queue.get()--------------------------爬取:requests.get解析:xpath语法--------------------------返回一个列表文件data1'''data1 = []self.lock.acquire()page_url = self.page_queue.get()print(page_url)self.lock.release()req1 = requests.get(page_url, headers=headers)page_text = req1.texthtml = etree.HTML(page_text)index_s = html.xpath('//*[@id="routerView"]/div[2]/ul/li/div[1]/p/text()')data1.append(index_s)names = html.xpath('//*[@id="routerView"]/div[2]/ul/li/div[1]/div[2]/p[1]/text()')data1.append(names)authors = html.xpath('//*[@id="routerView"]/div[2]/ul/li/div[1]/div[2]/p[2]/a/text()')data1.append(authors)scores = html.xpath('//*[@id="routerView"]/div[2]/ul/li/div[1]/div[2]/p[3]/span[1]/text()')data1.append(scores)numbers = html.xpath('//*[@id="routerView"]/div[2]/ul/li/div[1]/div[2]/p[3]/span[4]/em/text()')data1.append(numbers)infos = html.xpath('//*[@id="routerView"]/div[2]/ul/li/div[1]/div[2]/p[4]/text()')data1.append(infos)search = page_url.split('/')[-1]data1.append(search)return data1def spider2(self):'''输入:ajax_queue.get()--------------------------爬取:requests.get解析:re正则表达式语法--------------------------返回一个列表文件data2'''data2 = []self.lock.acquire()ajax_url = self.ajax_queue.get()print(ajax_url)self.lock.release()req2 = requests.get(ajax_url, headers=headers)resp = req2.textindex_s = re.findall(index_path, resp)data2.append(index_s)names = re.findall(name_path, resp)data2.append(names)authors = re.findall(author_path, resp)data2.append(authors)scores = re.findall(score_path, resp)scores_convert = []for score in scores:score = str(float(score) / 10)scores_convert.append(score)data2.append(scores_convert)numbers = re.findall(number_path, resp)data2.append(numbers)infos = re.findall(info_path, resp)data2.append(infos)search = ajax_url.split('/')[-1].split('?')[0]data2.append(search)start_index = ajax_url.split('=')[-1]data2.append(start_index)return data2class spider1(threading.Thread):def __init__(self, page_queue, lock, *args, **kwargs):super(spider1, self).__init__(*args, **kwargs)self.page_queue = page_queuedef run(self):if self.page_queue.empty() != True:data1 = crawl_and_parse.spider1()save_excel.writeData1(data1)else:returnclass spider2(threading.Thread):def __init__(self, ajax_queue, lock, *args, **kwargs):super(spider2, self).__init__(*args, **kwargs)self.ajax_queue = ajax_queuedef run(self):if self.ajax_queue.empty() != True:data2 = crawl_and_parse.spider2()save_excel.writeData2(data2)else:returnif __name__ == '__main__':start_time = time.time()page_queue = queue.Queue(17)ajax_queue = queue.Queue(200)lock = threading.Lock()save_excel = save_excel()crawl_and_parse = crawl_and_parse(page_queue, ajax_queue, lock)for page_url in page_urls:page_queue.put(page_url)for ajax_url in ajax_urls:ajax_queue.put(ajax_url)for i in range(20):th1 = spider1(page_queue, lock, name="爬虫1线程%d" % i)thread_list.append(th1)for j in range(200):th2 = spider2(ajax_queue, lock, name="爬虫2线程%d" % j)thread_list.append(th2)for t in thread_list:t.start()t.join()save_excel.saveData()end_time = time.time()print('it costs {}s'.format(end_time - start_time))

我执行了一下,花费了27.73451066017151s,应该还有很多可以优化的点。

6.结果显示

初学者,水平有限,有问题的地方恳求大佬们的指正,轻喷!!!希望这篇文章可以帮到大家。再会~~~~

本文标签: 微信读书top100