爬取某东600多本书籍,用数据帮你分析哪些Python书籍值得选择(上)
點擊“小詹學Python”,選擇“置頂”公眾號
重磅干貨,第一時間送達
本文轉載自Python全家桶,禁二次轉載
最近有好幾個讀者私下問我:剛接觸Python、或打算要學習Python,不知道選什么書比較合適,當時只根據自己的Python經驗和學習感受,給讀者推薦了一些自認為不錯的。但是,畢竟一個人接觸少,局限性太大,也許還有更多、更好的好書只是我沒有接觸過。于是就打算實際操作,通過爬蟲方式爬取某東上的書籍、通過數據來幫助大家更科學、更合理的選擇學習資料。
本文概要
說真的,在互聯網爆發的今天,想要找一本Python書那真的太簡單了,去某東或某寶,隨便敲一個Python,各種各樣的書籍撲面而來。好的是可選擇性多了,壞的是面對這些層次不齊的書,到底該選擇那一本,于是就有了這篇文章。
本篇文章分為上、下兩篇,今天是上篇,主要分享如何爬取書籍信息
上篇主要是分享如何通過Python爬取某東上的書籍信息
下篇主要是通過對爬取的數據進行分析,幫大家尋找一些口碑和銷量都不錯的書籍。
再看看需要爬取的書籍指標:
書名:買書必須要看的參數
價格:價格毋庸置疑,是必須要考慮的參數
評論數:側面反映書的好壞。某東的書籍無銷量,可以通過評論數來反映銷量
好評率:最直觀的指標,能夠反應讀者使用的體驗
排名:某東自營書籍有的會有銷量榜排名,所以也是非常重要的參數
是否自營:自營書籍會有書籍的排名,并且物流有保證
環境搭建
在開始爬蟲之前,先要搭建一個爬蟲的虛擬環境。我是在window10系統,使用anconda進行環境管理,大家可以根據自己的系統和操作習慣自行選擇。
爬蟲環境:Windows10 + Anconda + Python3.7 + Request
本文虛擬環境:python_books_env
需要安裝的包:request、matplotlib、pandas、fake_useragent、lxml
頁面分析
再明確了本文要爬取得參數之后,接下來爬蟲才算正式開始。首先,我們根據當前的需求,去某東商城找到對應的請求初始URL。本文主要是爬取python書籍,所以直接打開某東商城,在搜索欄直接輸入:python,就會出現我們需要的Python書籍:
這個只是搜索頁界面的url,當你在點擊頁面下面的第一頁時,你會發現第一頁面的url會反生變化,然后在點擊第二頁、第三頁的時候,url的如下:
# 前三頁的url
https://search.jd.com/Search?keyword=python&enc=utf-8&qrst=1&rt=1&stop=1&vt=2&wq=python&page=1&s=1&click=0
https://search.jd.com/Search?keyword=python&enc=utf-8&qrst=1&rt=1&stop=1&vt=2&wq=python&page=3&s=57&click=0
https://search.jd.com/Search?keyword=python&enc=utf-8&qrst=1&rt=1&stop=1&vt=2&wq=python&page=5&s=117&click=0
對比這三個url,發現page、s這兩個參數隨著每次的翻頁,會發生變化。但是作為一個上過高中、學過等差數列的我,
一下子就找到了翻頁規律:2n-1
但是問題又來了,s是個什么東西,大大的三個問號在我心里。不過不要急,當試著去掉這個s參數時,界面居然沒有發生變化,這個參數是用來搞笑的嗎?不過對于爬蟲來說,這種對數據無影響的參數直接干掉就是了(click這個參數也對數據沒影響,直接干掉)。
于是,通用的請求的url如下:
https://search.jd.com/Search?keyword=python&
enc=utf-8&qrst=1&rt=1&stop=1&vt=2&wq=python
&page=2n-1
上邊搞定翻頁之后,接下來就是對每一本書參數進行提取了。右鍵查看源碼,隨機看一本書,書名、價格、詳情頁url、是否自營這4個參數直接在當前頁面響應中就可以提取的到,但發現評論數這個便簽顯示
是空的,某有錯。
▲查看源碼評論截圖▲
不過不要慌,問題不大。經分析評論相關的數據是通過Ajax請求動態加載的,對于這種情況,直接右鍵選擇檢查, 通過NetWork抓包分析,很快就找到了評論的請求路徑:
不過評論的url里面包含referenceIds、_這兩個參數。對于抓包的url里面referenceIds是所有書籍的sku_id,返回的是當前頁所有書的評論信息。而我在抓取數據的時候,為了保證評論數正確性和代碼的可讀性,請求只攜帶當前書籍sku_id,返回當前書籍的評論數和好評率。_參數是時間戳,很好處理。
輕輕松松搞定了評論
所以,通用的評論請求的url如下:
"https://sclub.jd.com/comment/productCommentSummaries.action?referenceIds={}&callback=jQuery5954857&_={}"
3. 詳情頁獲取銷量排名
自營書的銷量排名數據是在詳情頁面,所以這次需要進入詳情頁去一探究竟。還是剛才的操作,先看了一波源碼,毛都沒看到。不過結合之前評論的操作,繼續右鍵檢查,經過分析,排名參數也是通過Ajax請求動態加載的,
嗖嗖的就搞定了
經過分析和驗證,只需要將找到的url里面參數skuId換成動態的就行。別的參數不用再做處理。
到這里,最后一個參數也分析完畢,通用url如下:
https://c.3.cn/book?skuId={}&cat=1713,3287,3797&area=1_72_2799_0
&callback=book_jsonp_callback
Show Code
越往后書籍,書的銷量和質量都在下降,所以這里只爬取前20頁的。經過上邊的分析,整個爬蟲的頁面分析已經逼逼完成,接下來就是代碼的展示。
好的,不逼逼了,直接操家伙,上代碼:
import requests
from lxml import etree
from userAgent import USER_AGENT_LIST
import random
import time
import re
import os
import csv
from fake_useragent import UserAgent
# 實例化一個ua對象
ua = UserAgent()
class PythonBookSpider(object):
? ? """爬取京東商城前20頁的Python書籍"""
? ? def __init__(self):
? ? ? ? self.base = "https://search.jd.com/Search?keyword=python&enc=utf-8&qrst=1&rt=1&stop=1&vt=2&wq=python&page={}"
? ? ? ? self.comment_url = "https://sclub.jd.com/comment/productCommentSummaries.action?referenceIds={}&"
? ? ? ? ? ? ? ? ? ? ?"callback=jQuery5954857&_={}"
? ? ? ? self.rank_url = "https://c.3.cn/book?skuId={}&cat=1713,3287,3797&area=1_72_2799_0&callback=book_jsonp_callback"
? ? ? ? self.headers = {
? ? ? ? ? ? ? "User-Agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) "
? ? ? ? ? ? ? "Chrome/22.0.1207.1 Safari/537.1",
? ? ? ? ? ? ? "authority": "search.jd.com"
? ? ? ? ? ? ?}
? ? def _send_request(self, url):
? ? ? ? """
? ? ? ? 發送請求,獲取響應
? ? ? ? :param url: 請求路徑
? ? ? ? :return:
? ? ? ? """
? ? ? ? # self.headers["User-Agent"] = random.choice(USER_AGENT_LIST)
? ? ? ? self.headers["User-Agent"] = ua.random
? ? ? ? time.sleep(0.5)
? ? ? ? response = requests.get(url=url, headers=self.headers, timeout=5)
? ? ? ? return response
? ? ?def send_request(self, url):
? ? ? ? """主要是對請求響應進行處理"""
? ? ? ? try:
? ? ? ? ? ? response = self._send_request(url)
? ? ? ? except Exception as e:
? ? ? ? ? ? print("request error: {}".format(e))
? ? ? ? ? ? return None
? ? ? ? if response.status_code != 200:
? ? ? ? ? ? content = None
? ? ? ? else:
? ? ? ? ? ?content = response.content
? ? ? ? return content
? ? def get_comment_count(self, sku_id):
? ? ? ? """獲取評論數"""
? ? ? ? print("comment url: {}".format(self.comment_url.format(sku_id, int(time.time()))))
? ? ? ? response = self.send_request(self.comment_url.format(sku_id, int(time.time())))
? ? ? ? if not response:
? ? ? ? ? ? return "", ""
? ? ? ? # 響應的編碼方式可以在響應中查看
? ? ? ? response = response.decode("GBK")
? ? ? ? good_rate = re.findall(""GoodRate":(d.d+)", response)[0] if re.findall(""GoodRate":(d.d+)",
? ? ? ? ? ? ? ? ?response) else ""
? ? ? ? commet_count = re.findall(""CommentCount":(d+)", response)[0] if re.findall(""CommentCount":(d+)",
? ? ? ? ? ? ? ? ?response) else ""
? ? ? ? # print(" good rate: {}".format(good_rate))
? ? ? ? # print(" comment count: {}".format(commet_count))
? ? ? ? return good_rate, commet_count
? ? def parse_book_rank(self, sku_id):
? ? ? ? """
? ? ? ? 獲取京東自營書籍銷量排行榜名次
? ? ? ? :param sku_id: int 書籍的sku_id
? ? ? ? :return:
? ? ? ? """
? ? ? ? # b'book_jsonp_callback({"yn":2,"rank":86,"ebookId":0})'
? ? ? ? response = self.send_request(self.rank_url.format(sku_id))
? ? ? ? if not response:
? ? ? ? ? ? return False, None
? ? ? ? print("b_rank:{}".format(response.decode()))
? ? ? ? b_rank = re.findall(r""rank":[-|0-9][0-9]*", response.decode())
? ? ? ? b_rank = b_rank[0].split(":")[1] if b_rank else ""
? ? ? ? return True, b_rank
? ? def save_book_info(self, books_list):
? ? ? ? """
? ? ? ? 保存書籍信息
? ? ? ? :param files: 每一頁的書籍信息
? ? ? ? """
? ? ? ? if not os.path.exists("./PythonBookInfo.csv"):
? ? ? ? ? ? with open("./PythonBookInfo.csv", "a+", newline="") as file:
? ? ? ? ? ? ? ? writer = csv.writer(file)
? ? ? ? ? ? ? ? writer.writerow(["name", "price", "is_self_operated", "comment_counts", "good_comments_rate",
? ? ? ? ? ? ? ? ? ? ?"good_comments_rate", "sale_rank"])
? ? ? ? with open("./PythonBookInfo.csv", "a+", newline="") as file:
? ? ? ? ? ? writer = csv.writer(file)
? ? ? ? ? ? for book_list in books_list:
? ? ? ? ? ? ? ? try:
? ? ? ? ? ? ? ? ? ? writer.writerows(book_list)
? ? ? ? ? ? ? ? except:
? ? ? ? ? ? ? ? ? ? continue
? ? def parse_index_page(self, response):
? ? ? ? """
? ? ? ? 解析首頁的界面信息
? ? ? ? :param response: type: str 搜索頁面的響應
? ? ? ? :return: type: list 每一頁的全部書籍相關信息
? ? ? ? """
? ? ? ? index_selector = etree.HTML(response)
? ? ? ? books_list = index_selector.xpath('//div[@id="J_goodsList"]/ul/li') # 解析每一頁的書籍列表
? ? ? ? py_bookinfo_list = []
? ? ? ? for book in books_list:
? ? ? ? ? ? # 書籍詳情地址
? ? ? ? ? ? b_url = book.xpath('.//div[@class="p-img"]/a/@href')
? ? ? ? ? ? # 圖書價格
? ? ? ? ? ? b_price = book.xpath('.//div[@class="p-price"]//i/text()')
? ? ? ? ? ? # 賣家方式
? ? ? ? ? ? b_seller = book.xpath('//div[@class="p-icons"]/i[1]/text()')
? ? ? ? ? ? # 書名稱
? ? ? ? ? ? b_name = book.xpath('.//div[@class="p-name"]//em')
? ? ? ? ? ? # print("b_name: {}".format(b_name[0].xpath("string(.)")))
? ? ? ? ? ? b_name = [] if not b_name else b_name[0].xpath("string(.)").strip()
? ? ? ? ? ? # 書的評論數:通過js加載的
? ? ? ? ? ? sku_id = book.xpath('./@data-sku')[0]
? ? ? ? ? ? if not sku_id:
? ? ? ? ? ? ? ? continue
? ? ? ? ? ? good_rate, commet_count = self.get_comment_count(sku_id)
? ? ? ? ? ? if not all([b_url, b_price, good_rate, commet_count, b_name]):
? ? ? ? ? ? ? ? continue
? ? ? ? ? ? detail_url = "https:" + b_url[0] if not b_url[0].startswith("https") else b_url[0]
? ? ? ? ? ? # print("detail url:{}".format(detail_url))
? ? ? ? ? ? # 如果是京東自營的話,在抓取對應的自營排名、出版社
? ? ? ? ? ? if b_seller[0] == "自營":
? ? ? ? ? ? ? ? # 獲取書籍銷售排行榜名次
? ? ? ? ? ? ? ? rank_response = self.parse_book_rank(sku_id)
? ? ? ? ? ? ? ? if not rank_response:
? ? ? ? ? ? ? ? ? ? continue
? ? ? ? ? ? ? ? b_rank = rank_response[1]
? ? ? ? ? ? ? ? b_seller = b_seller[0]
? ? ? ? ? ? ? ? # 獲取書籍出版社
? ? ? ? ? ? ? ? # b_publisher = self.parse_detail_page(detail_url)
? ? ? ? ? ? else:
? ? ? ? ? ? ? ? b_rank = ""
? ? ? ? ? ? ? ? b_seller = ""
? ? ? ? ? ? py_bookinfo_list.append([[b_name, b_price[0], b_seller, commet_count, good_rate, b_rank, detail_url]])
? ? ? return py_bookinfo_list
? ? def spider(self):
? ? ? ? """spider的主要邏輯業務"""
? ? ? ? for page in range(1, 21):
? ? ? ? ? ? # 1.請求搜索頁,獲取書籍列表頁面信息,這里請求前20頁
? ? ? ? ? ? first_response = self.send_request(self.base.format(2 * page - 1))
? ? ? ? ? ? if not first_response:
? ? ? ? ? ? ? ?continue
? ? ? ? ? ? # 2.解析搜索頁書籍的相關信息
? ? ? ? ? ? py_bookinfo_list = self.parse_index_page(first_response)
? ? ? ? ? ? if not py_bookinfo_list:
? ? ? ? ? ? ? ? continue
? ? ? ? ? ? # 3.保存爬取書籍信息
? ? ? ? ? ? self.save_book_info(py_bookinfo_list)
? ? ? ? ? ? print("第 {}頁爬取完成".format(page))
? ? ? ? print("抬頭 望天")
if __name__ == '__main__':
? ? py_spider = PythonBookSpider()
? ? py_spider.spider()
整篇文章爬蟲部分就已經完成了。接下來就是運行該文件,爬取書籍信息了。爬到的書籍一部分信息如下:
至此,這篇文章的上篇就分享到這里,接下來會分享下篇,主要是通過對爬到的書籍進行分析,找出性價比更搞的書籍,幫助大家在選擇買書的時候可以多一份參考,少一分焦慮。
總結
以上是生活随笔為你收集整理的爬取某东600多本书籍,用数据帮你分析哪些Python书籍值得选择(上)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 浙大提出会打德扑的「自我博弈」AI,还会
- 下一篇: 春招,这 110 道 Python 面试