python實作爬取網易雲音樂評論,并且将評論資訊存儲到pymysql
第一步:我們要準備好我們的環境
python編輯器
python3.6
隻要是python3都行
我們這次抓取是通過網易雲特定的API進行一個請求并且抓取的
網易雲API:
擷取評論的API
http://music.163.com/api/v1/resource/comments/R_SO_4_{歌曲ID}?limit={每頁限制數量}&offset={評論數總偏移}
擷取評論對應使用者的資訊API:
https://music.163.com/api/v1/user/detail/{使用者ID}
https://music.163.com/api/v1/resource/comments/R_SO_4_ 上面這部分是官方接口的基礎URL,由于網易雲評論是Ajax類型,是以我們去評論的界面右擊檢查,單擊R_SO_4_’ + str(songid) + '?csrf_token=這個包,發現表單資料為
由此可知,當我們對網易雲評論界面發起請求時,需要加入這部分的表單資料。通過構造URL之後,便可對其發起請求,得到json格式資料。
我們通過上面的API建構一個配置檔案,config.py
config.py
import re
SONGID = '190449'
SONGNAME = '吻别'
LIMIT_NUM = 100
PATTERN = re.compile(r'[\n\t\r\/]') #替換掉評論中的特殊字元以防插入資料庫時報錯
#資料庫名
DATABASE = '****'
#資料庫表名
TABLE_COMMENTS = '****'
HOST = '****'
USER = '****'
PASSWD = '****'
ROOT_URL = 'http://music.163.com/api/v1/resource/comments/R_SO_4_'+SONGID+'?limit='+str(LIMIT_NUM)+'&offset=%s'
HEADERS = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36',
'Host': 'music.163.com',
#自己本地電腦的cookie,用來識别
'Cookie': '****',
}
在抓取,我們要創自己本地的資料庫:
我先用常用的資料庫類型建立了資料庫:
CREATE TABLE IF NOT EXISTS `comments`(
`id` int(100) auto_increment primary key,
`commentId` INT NOT NULL,
`content` VARCHAR(100) NOT NULL,
`likedCount` VARCHAR(40) NOT NULL,
`time` TIMESTAMP,
`userId` int(100) NOT NULL,
`songId` int(100) NOT NULL,
`songName` VARCHAR(40) NOT NULL
)ENGINE=InnoDB DEFAULT CHARSET=utf8;
這個上面的語句與下面的抓取檔案在存儲的時候産生了不少的沖突:
第一個問題:
但是資料可以存儲到資料庫,不過commentId這個資料沒有在資料庫裡面,
這個問題是:
類型不比對,commentId int類型長度不比對,超出範圍的問題,
我原本用int ,int 預設是11的長度,我後面用int(100)也不行
于是我用了類型為bigint時,可以插入 id =1113333333333的這條資料
成功解決問題
第二個問題:
這個也是一個類型不比對問題:
解決方法:
自己的表的content字段 長度不夠,則改資料庫中content類型為 mediumtext類型即可
在存儲過程後還會出現另外一個問題就是遇到一些特殊字元是會出現一個字元存儲不了的問題,這個是一個編碼格式問題
CHARSET=utf8
我們把編碼格式改為:
CHARSET=utf8mb4就可以
也可以在建立資料庫後通過:
alter table 表名 convert to character set utf8mb4;
修改
經過解決這些問題後,我們建立正确的資料庫語句為:
CREATE TABLE IF NOT EXISTS `comments`(
`id` int(100) auto_increment primary key,
`commentId` BIGINT NOT NULL,
`content` mediumtext NOT NULL,
`likedCount` VARCHAR(40) NOT NULL,
`time` TIMESTAMP,
`userId` BIGINT NOT NULL,
`songId` BIGINT NOT NULL,
`songName` VARCHAR(40) NOT NULL
)ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
或者:
CREATE TABLE IF NOT EXISTS `comments`(
`id` int(100) auto_increment primary key,
`commentId` BIGINT NOT NULL,
`content` mediumtext NOT NULL,
`likedCount` VARCHAR(40) NOT NULL,
`time` TIMESTAMP,
`userId` BIGINT NOT NULL,
`songId` BIGINT NOT NULL,
`songName` VARCHAR(40) NOT NULL
)ENGINE=InnoDB DEFAULT CHARSET=utf8;
alter table 表名 convert to character set utf8mb4;
修改
上面就是我在建立資料庫檔案中,遇到到了不少的坑,以及解決方法。
下面是我們抓取的主函數:
crawler.py
# -*- coding=utf-8 -*-
import json
from datetime import datetime
import requests
import config
import pymysql
import gevent
from gevent import monkey
monkey.patch_all()
class Crawler(object):
def run(self, url):
print('crawl ', url)
self.parse_page(url)
def down(self,url):
try:
return requests.get(url=url, headers=config.HEADERS).text
except Exception as e:
print('down err>>>', e)
def parse_page(self, url):
content = self.down(url)
js = json.loads(content)
datas = []
for c in js['comments']:
data = {}
try:
data['commentId'] = c['commentId']
data['content'] = config.PATTERN.sub('', c['content'])
data['likedCount'] = int(c['likedCount'])
data['time'] = datetime.fromtimestamp(c['time']//1000)
data['userId'] = c['user']['userId']
datas.append(data)
except Exception as e:
print('解析js出錯>>>', e)
self.save(datas)
def save(self, datas):
conn = pymysql.connect(host=config.HOST, user=config.USER, passwd=config.PASSWD, db=config.DATABASE, charset='utf8mb4') # 注意字元集要設為utf8mb4,以支援存儲評論中的emoji表情
cursor = conn.cursor()
sql = 'insert into '+config.TABLE_COMMENTS+' (commentId,content,likedCount,time,userId,songId,songName) VALUES (%s,%s,%s,%s,%s,%s,%s)'
for data in datas:
try:
# cursor.execute('SELECT max(id) FROM '+config.TABLE_COMMENTS)
# s = cursor.fetchone()[0]
# if s:
# id_ = s+1
# else:
# id_ = 1
cursor.execute(sql, (data['commentId'], data['content'], data['likedCount'], data['time'], data['userId'], config.SONGID, config.SONGNAME))
conn.commit()
except Exception as e:
print('存儲錯誤>>>', e)
cursor.close()
conn.close()
def main(self, pages):
url_list = [config.ROOT_URL%(num*config.LIMIT_NUM) for num in range(0, pages//config.LIMIT_NUM+1)]
job_list = [gevent.spawn(self.run, url) for url in url_list]
gevent.joinall(job_list)
def getTotal():
try:
req = requests.get(config.ROOT_URL%(0), headers=config.HEADERS).text
js = json.loads(req)
return js['total']
except Exception as e:
print(e)
return None
if __name__=="__main__":
total = getTotal()
spider = Crawler()
spider.main(total)
成功運作:
網易雲官方對于API的請求是有限制的,頻繁通路會被封禁ip