最近和朋友一起开发APP,需要大量数据,而"互联网"与"共享"融合发展的理念,遂资源的可重用给予了当代骚客文人获得感与幸福感…好了,不日白了(正宗重庆话,吹牛的意思),开始正题
BeautifulSoup4
本人是做JavaWeb的,可能多多少少还是遗留了Java的一些格式及规范,但爬虫千千万,却是Python最好使
- Beautiful Soup4作为HTML/XML的解析器,其使用、解析难度都较为简单;
- 人性化的API,支持 lxml 的 XML解析器;
- 同样也支持CSS选择器、Python标准库中的HTML解析器;
- 在整个DOM树中,能够快速定位到理想位置节点,并获取相应的内容;
- 自动将输入文档转换为Unicode编码,输出文档转换为utf-8编码;
- 安装:pip install beautifulsoup4;
Selenium + ChromeDriver与Requests
- Selenium是一个用于Web应用程序测试的开源自动化工具。直接运行在浏览器中,就像真正的用户在操作一样。而对于爬虫来讲,可视化的界面加模拟用户操作,简直是质的提升。
- ChromeDriver - WebDriver for Chrome,WebDriver是一个开源工具,用于在许多浏览器上自动测试webapps。它提供了导航到网页,用户输入,JavaScript执行等功能。ChromeDriver是一个独立的服务,它为 Chromium 实现 WebDriver 的 JsonWireProtocol 协议。
- Requests模块比urllib2模块更简洁,支持HTTP连接保持和连接池,支持使用cookie保持会话,支持文件上传,支持自动响应内容的编码,支持国际化的URL和POST数据自动编码。在python内置模块的基础上进行了高度的封装,从而使得python进行网络请求时,变得人性化。
- selenium安装:pip install selenium。
- ChromeDriver安装:下载地址:http://chromedriver.storage.googleapis.com/index.html或https://npm.taobao.org/mirrors/chromedriver/。
(1)首先解压压缩包
(2)找到chromedriver.exe并复制到你的项目中的venv\Lib\site-packages\selenium\webdriver\chrome中 - Requests安装:pip install requests
消息头与代理IP以及请求频率
作为爬虫(非用户操作),是一定要学会伪装自己,不然仅仅是写代码测试的时候,多搞几下,人家网站的反爬措施就会冻结你一段时间,那就直接玩鸟了。
- 消息头:一般就是针对于headers中的User-Agent,如果没有对headers进行设置,User-Agent会声明自己是python脚本,而遭到网站的拒绝。
修改自己的headers可以将自己的爬虫脚本伪装成浏览器的正常访问,从而避免这一问题。
# 较常见的headers
headers = {
'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.116 Safari/537.36'
}
# 或者你心情好,也可以搞个完善的headers,like this
headers = {
'Accept': '*/*',
'Accept-Language': 'en-US,en;q=0.8',
'Cache-Control': 'max-age=0',
'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.116 Safari/537.36',
'Connection': 'keep-alive',
'Referer': 'http://www.baidu.com/'
}
- 设置代理IP:我们可以想象程序的运行速度是非常快的,当我们用爬虫获取某个网站的数据时,一个固定IP的访问频率远超人为操作的标准(即使你单身几十年,也不可能在几ms内,进行如此频繁的访问),所以网站一般会设置一个IP访问频率的阈值,一旦超出,就证明这是机器在搞骚操作。
推荐西刺代理IP,
proxy = {'http':'106.46.136.112:808'}
- 请求频率:通俗点也可以是访问次数。前面提到,以非人为操作的频率访问别人的网站,本身这对于服务器来讲是一种压力,作为爬虫,应该尽量避免这种荒唐的事情发生,所以,适当减少自己的请求次数与请求节奏,对你对服务器,都是百利而无一害的。
最简单的做法,可以让你的程序休息一会儿
import time
time.sleep(3000)
MongoDB
在此要感谢我的朋友TH,起初我只知道MongoDB这几个英文字母,是他作为引路人和实践者,让我认知到又一门技术,非常感谢你的引导!
- MongoDB是一个内存数据库,对数据的操作大部分都在内存中,支持冷/热数据处理,是一个基于分布式文件存储的开源数据库系统,在高负载的情况下,添加更多的节点,以保证服务器性能。
- 将数据存储为一个文档(即可以存放 xml、json、bson 类型的数据),数据结构由键值(key=>value)对组成。MongoDB 文档类似于 JSON 对象。字段值可以包含其他文档,数组及文档数组。
- 由此可见,相比较 MySQL,MongoDB 以一种直观文档的方式来完成数据的存储。它很像 JavaScript 中定义的 JSON 格式,不过数据在存储的时候 MongoDB 数据库为文档增加了序列化的操作,最终存进磁盘的其实是一种叫做 BSON 的格式,即 Binary-JSON。
- 存储方式为虚拟内存+持久化。支持大容量的存储,适合文件、图片等数据。
- MongoDB安装:https://www.mongodb.com/download-center/community;下载适合你操作系统及版本的安装包,具体流程可以百度。语法及使用具体操作,在这里就不做概述。
最后献上代码,有误或可以调优的地方,还请留言指出,一定虚心接受
# -*- coding:utf-8 -*-
import json
import re
import time
import random
import requests
import pymongo
import bson.binary
from selenium import webdriver
from bs4 import BeautifulSoup
BASE_URL = "https://music.xxx.com"
HEADER = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36"
}
class GetData():
def __init__(self):
# 建立MongoDB数据库连接
client = pymongo.MongoClient('192.168.31.68', 27017)
# 连接所需数据库
db = client["song"]
# 连接所需集合
self.lyric = db["lyric"]
self.picture = db["picture"]
@staticmethod
def get_driver():
""" 获取Chrome driver """
# 进入阅览器
options = webdriver.ChromeOptions()
# 设置header
options.add_argument('user-agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36"')
# 加载chromedriver.exe
chrome_driver = r"D:\myProject\worker\venv\Lib\site-packages\selenium\webdriver\chrome\chromedriver.exe"
driver = webdriver.Chrome(executable_path=chrome_driver, chrome_options=options)
return driver
@staticmethod
def get_chrome(driver, url):
""" 获得HTML页面内容 """
time.sleep(random.randint(10, 30)) # 主线程随机暂停 10至30秒
driver.get(url)
iframe = driver.find_elements_by_tag_name('iframe')[0]
driver.switch_to.frame(iframe)
return BeautifulSoup(driver.page_source, "lxml")
@staticmethod
def get_request(url):
""" 封装request的GET请求 """
time.sleep(random.randint(10, 30)) # 主线程随机暂停 10至30秒
data = requests.get(url, headers=HEADER)
return data
@staticmethod
def get_lrc(song_id):
""" 解析拼装歌词 """
lrc_url = 'http://music.xxx.com/api/song/lyric?' + 'id=' + str(song_id) + '&lv=1&kv=1&tv=-1'
html = GetData.get_request(lrc_url).text
# 转换为json
j = json.loads(html)
# 英文歌词
lrc = j['lrc']['lyric']
# 中文歌词
cn_lrc = j['tlyric']['lyric']
# 正则去掉[]及里面内容
pat = re.compile(r'\[.*\]')
lrc = re.sub(pat, "", lrc)
cn_lrc = re.sub(pat, "", cn_lrc)
# 获得英文和中文歌词
lrc = lrc.strip()
cn_lrc = cn_lrc.strip()
a = lrc.split("\n")
b = cn_lrc.split("\n")
result = ""
for index_en in range(len(a)):
for index_cn in range(len(b)):
if index_en == index_cn:
result += a[index_en] + "\n" + b[index_en] + "\n"
return result
def get_netease_cloud(self, count):
driver = GetData.get_driver()
# 只搜索类型为欧美的歌曲
url = BASE_URL + "/#/discover/playlist/?order=hot&cat=欧美&limit=35&offset=" + str((0 + count) * 35)
soup = GetData.get_chrome(driver, url)
# img_html = soup.find_all('img', class_=re.compile('j-flag'))
# img_data = GetData.get_request(img_html[count]['src'])
# 过滤class为msk的a标签,检索出每张专辑ID及访问路径
msg = soup.find_all('a', class_=re.compile('msk'))
for playlist in msg:
""" 单张专辑的歌曲目录 """
url = BASE_URL + playlist['href']
soup = GetData.get_chrome(driver, url)
# 获取table列表中播放按钮的ID值
play = soup.find_all('span', class_=re.compile('ply'))
for play_id in play:
try:
url = BASE_URL + "/song?id=" + play_id['data-res-id']
soup = GetData.get_chrome(driver, url)
# 获取图片
song_img = soup.find_all('img', class_=re.compile('j-img'))
song_img_data = GetData.get_request(song_img[0]['data-src']).content
# 歌名
song_name = soup.find_all('em', class_=re.compile('f-ff2'))
# 歌手名与所属专辑
singer, album = soup.find_all('p', class_=re.compile('des s-fc4'))
pat = re.compile('>(.*?)<')
pat_song_name = ''.join(pat.findall(str(song_name[0])))
pat_singer = ''.join(pat.findall(str(singer)))
pat_album = ''.join(pat.findall(str(album)))
result = "歌名:" + pat_song_name + "\n" + pat_singer + "\n" + pat_album + "\n" + GetData.get_lrc(
play_id['data-res-id'])
self.picture.save(dict(
song_name=pat_song_name,
img=bson.binary.Binary(song_img_data)
))
self.lyric.save(dict(
song_name=pat_song_name,
singer=pat_singer,
album=pat_album,
lyric=result
))
except Exception as e:
print(e)
pass
continue
driver.quit()
if __name__ == '__main__':
count = 0
lic = GetData()
while(count < 38):
lic.get_netease_cloud(count)
count += 1