关注的专栏:http://blog.csdn.net/column/details/15321.htmlinclude
他的个人博客:http://cuijiahua.com/
一、网络爬虫简介
网络爬虫,也叫网络蜘蛛(Web Spider)。它根据网页地址(URL)爬取网页内容,而网页地址(URL)就是我们在浏览器中输入的网站链接。比如:https://www.baidu.com/,它就是一个URL。
1.1、审查元素
在浏览器的地址栏输入URL地址,在网页处右键单击,找到检查(不同浏览器的叫法不同,Chrome浏览器叫做检查,Firefox浏览器叫做查看元素)
浏览器就是作为客户端从服务器端获取信息,然后将信息解析,并展示给我们的。我们可以在本地修改HTML信息,为网页"整容",但是我们修改的信息不会回传到服务器,服务器存储的HTML信息不会改变。刷新一下界面,页面还会回到原本的样子。
1.2、练习实例
第一步:
根据URL,获取网页的HTML信息。在Python3中,可以使用urllib.request和requests进行网页爬取。
urllib库是python内置的,只要安装了Python就可以使用这个库。
requests库是第三方库。
requests库强大好用,本文使用requests库获取网页的HTML信息。requests库的github地址:https://github.com/requests/requests
(1)requests安装
在cmd中,使用如下指令(二选一)安装requests库:
pip install requests
easy_install requests
# -*- coding:UTF-8 -*-
import requests
if __name__ == '__main__':
target = 'http://gitbook.cn/'
req = requests.get(url=target) #向服务器发起GET请求,requests.get()方法就是从服务器获取数据
print(req.text)
1.3、爬虫实例1:小说下载(静态)
小说网站《笔趣看》URL:http://www.biqukan.com/ 下载《一念永恒》
步骤1:获取HTML
#/usr/bin/env.python
# -*- coding:UTF-8 -*-
import requests
if __name__ == '__main__':
target = 'http://www.biqukan.com/1_1094/5403177.html'
req = requests.get(url=target)
print(req.text)
步骤2:解析HTML信息,提取感兴趣的内容
方法:正则表达式、Xpath、Beautiful Soup等
Beautiful Soup指令安装(二选一):
pip install beautifulsoup4
easy_install beautifulsoup4
# -*- coding:UTF-8 -*-
from bs4 import BeautifulSoup
import requests
if __name__ == "__main__":
target = 'http://www.biqukan.com/1_1094/5403177.html'
req = requests.get(url = target)
html = req.text
bf = BeautifulSoup(html)
texts = bf.find_all('div', class_ = 'showtxt')
#find_all匹配的返回的结果是一个列表
print(texts[0].text.replace('\xa0'*8,'\n\n'))
#使用text属性,提取文本内容,滤除br标签,replace方法,剔除空格,替换为回车进行分段
print(texts[0].text.replace('\xa0'*8,'\n\n'))
#使用text属性,提取文本内容,滤除br标签,replace方法,剔除空格,替换为回车进行分段
整合代码:
#/usr/bin/env.python
# -*- coding:UTF-8
from bs4 import BeautifulSoup
import requests, sys
class downloader(object):
def __init__(self):
self.server = 'http://www.biqukan.com/'
self.target = 'http://www.biqukan.com/1_1094/'
self.names = [] # 存放章节名
self.urls = [] # 存放章节链接
self.nums = 0 # 章节数
# 函数说明:获取下载链接
def get_download_url(self):
req = requests.get(url=self.target)
html = req.text
div_bf = BeautifulSoup(html)
div = div_bf.find_all('div', class_='listmain')
a_bf = BeautifulSoup(str(div[0]))
a = a_bf.find_all('a')
self.nums = len(a[15:]) # 剔除不必要的章节,并统计章节数
for each in a[15:]:
self.names.append(each.string)
self.urls.append(self.server + each.get('href'))
# 函数说明:获取章节内容
def get_contents(self, target):
req = requests.get(url=target)
html = req.text
bf = BeautifulSoup(html)
texts = bf.find_all('div', class_='showtxt')
texts = texts[0].text.replace('\xa0' * 8, '\n\n')
return texts
"""
函数说明:将爬取的文章内容写入文件
Parameters:
name - 章节名称(string)
path - 当前路径下,小说保存名称(string)
text - 章节内容(string)
"""
def writer(self, name, path, text):
write_flag = True
with open(path, 'a', encoding='utf-8') as f:
f.write(name + '\n')
f.writelines(text)
f.write('\n\n')
if __name__ == "__main__":
dl = downloader()
dl.get_download_url()
print('《一年永恒》开始下载:')
for i in range(dl.nums):
dl.writer(dl.names[i], '一念永恒.txt', dl.get_contents(dl.urls[i]))
sys.stdout.write(" 已下载:%.3f%%" % float(i / dl.nums) + '\r')
sys.stdout.flush()
print('《一年永恒》下载完成')
1.4、爬虫实例1:图片下载(动态)
图片网址:https://unsplash.com/
html规定,图片统统给我放到标签中,标签有很多属性,有alt、src、class、style属性,其中src属性存放的就是我们需要的图片保存地址,我们根据这个地址就可以进行图片的下载。(并不可以!!!),该网站为动态加载,动态加载有一部分的目的就是为了反爬虫。动态网站使用动态加载常用的手段就是通过调用JavaScript来实现的
1、抓包工具:帮我们分析。Fiddler。URL:http://www.telerik.com/fiddler
2、浏览器自带的Network
json格式存储传输的数据,json格式是一种轻量级的数据交换格式,起到封装数据的作用,易于人阅读和编写,同时也易于机器解析和生成
整合代码:
# -*- coding:UTF-8 -*-
import requests, json, time, sys
from contextlib import closing
class get_photos(object):
def __init__(self):
self.photos_id = []
self.download_server = 'https://unsplash.com/photos/xxx/download?force=trues'
self.target = 'http://unsplash.com/napi/feeds/home'
self.headers = {'authorization': 'your Client-ID'}
"""
函数说明:获取图片ID
"""
def get_ids(self):
req = requests.get(url=self.target, headers=self.headers, verify=False)
html = json.loads(req.text)
next_page = html['next_page']
for each in html['photos']:
self.photos_id.append(each['id'])
time.sleep(1)
for i in range(4):
req = requests.get(url=next_page, headers=self.headers, verify=False)
html = json.loads(req.text)
next_page = html['next_page']
for each in html['photos']:
self.photos_id.append(each['id'])
time.sleep(1)
"""
函数说明:图片下载
Parameters:
无
Returns:
无
Modify:
2017-09-13
"""
def download(self, photo_id, filename):
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.79 Safari/537.36'}
target = self.download_server.replace('xxx', photo_id)
with closing(requests.get(url=target, stream=True, verify=False, headers=self.headers)) as r:
with open('%d.jpg' % filename, 'ab+') as f:
for chunk in r.iter_content(chunk_size=1024):
if chunk:
f.write(chunk)
f.flush()
if __name__ == '__main__':
gp = get_photos()
print('获取图片连接中:')
gp.get_ids()
print('图片下载中:')
for i in range(len(gp.photos_id)):
print(' 正在下载第%d张图片' % (i + 1))
gp.download(gp.photos_id[i], (i + 1))
1.5、爬虫实例1:VIP视频下载
编写代码的时候注意一个问题,就是我们需要使用requests.session()保持我们的会话请求。简单理解就是,在初次访
问服务器的时候,服务器会给你分配一个身份证明。我们需要拿着这个身份证去继续访问,如果没有这个身份证明,
服务器就不会再让你访问。这也就是这个服务器的反爬虫手段,会验证用户的身份。
现在梳理一下编程思路:
用正则表达式匹配到key、time、url等信息。
根据匹配的到信息发POST请求,获得一个存放视频信息的url。
根据这个url获得视频存放的地址。
根据最终的视频地址,下载视频。
整合代码:
# -*- coding:utf-8 -*-
from tkinter.filedialog import askdirectory
from MyQR.myqr import run
from urllib import request, parse
from bs4 import BeautifulSoup
import tkinter.messagebox as msgbox
import tkinter as tk
import webbrowser
import re
import json
import os
import types
import requests
import time
"""
类说明:爱奇艺、优酷等实现在线观看以及视频下载的类
Parameters:
width - tkinter主界面宽
height - tkinter主界面高
Returns:
无
Modify:
2017-05-09
"""
class APP:
def __init__(self, width=500, height=300):
self.w = width
self.h = height
self.title = ' VIP视频破解助手'
self.root = tk.Tk(className=self.title)
self.url = tk.StringVar()
self.v = tk.IntVar()
self.v.set(1)
# Frame空间
frame_1 = tk.Frame(self.root)
frame_2 = tk.Frame(self.root)
frame_3 = tk.Frame(self.root)
# Menu菜单
menu = tk.Menu(self.root)
self.root.config(menu=menu)
filemenu = tk.Menu(menu, tearoff=0)
moviemenu = tk.Menu(menu, tearoff=0)
menu.add_cascade(label='菜单', menu=filemenu)
menu.add_cascade(label='友情链接', menu=moviemenu)
filemenu.add_command(label='使用说明', command=lambda: webbrowser.open(
'http://blog.csdn.net/c406495762/article/details/71334633'))
filemenu.add_command(label='关于作者', command=lambda: webbrowser.open('http://blog.csdn.net/c406495762'))
filemenu.add_command(label='退出', command=self.root.quit)
# 各个网站链接
moviemenu.add_command(label='网易公开课', command=lambda: webbrowser.open('http://open.163.com/'))
moviemenu.add_command(label='腾讯视频', command=lambda: webbrowser.open('http://v.qq.com/'))
moviemenu.add_command(label='搜狐视频', command=lambda: webbrowser.open('http://tv.sohu.com/'))
moviemenu.add_command(label='芒果TV', command=lambda: webbrowser.open('http://www.mgtv.com/'))
moviemenu.add_command(label='爱奇艺', command=lambda: webbrowser.open('http://www.iqiyi.com/'))
moviemenu.add_command(label='PPTV', command=lambda: webbrowser.open('http://www.bilibili.com/'))
moviemenu.add_command(label='优酷', command=lambda: webbrowser.open('http://www.youku.com/'))
moviemenu.add_command(label='乐视', command=lambda: webbrowser.open('http://www.le.com/'))
moviemenu.add_command(label='土豆', command=lambda: webbrowser.open('http://www.tudou.com/'))
moviemenu.add_command(label='A站', command=lambda: webbrowser.open('http://www.acfun.tv/'))
moviemenu.add_command(label='B站', command=lambda: webbrowser.open('http://www.bilibili.com/'))
# 控件内容设置
group = tk.Label(frame_1, text='请选择一个视频播放通道:', padx=10, pady=10)
tb1 = tk.Radiobutton(frame_1, text='通道一', variable=self.v, value=1, width=10, height=3)
tb2 = tk.Radiobutton(frame_1, text='通道二', variable=self.v, value=2, width=10, height=3)
label1 = tk.Label(frame_2, text="请输入视频链接:")
entry = tk.Entry(frame_2, textvariable=self.url, highlightcolor='Fuchsia', highlightthickness=1, width=35)
label2 = tk.Label(frame_2, text=" ")
play = tk.Button(frame_2, text="播放", font=('楷体', 12), fg='Purple', width=2, height=1, command=self.video_play)
label3 = tk.Label(frame_2, text=" ")
# download = tk.Button(frame_2, text = "下载", font = ('楷体',12), fg = 'Purple', width = 2, height = 1, command = self.download_wmxz)
QR_Code = tk.Button(frame_3, text="手机观看", font=('楷体', 12), fg='Purple', width=10, height=2,
command=self.QR_Code)
label_explain = tk.Label(frame_3, fg='red', font=('楷体', 12),
text='\n注意:支持大部分主流视频网站的视频播放!\n此软件仅用于交流学习,请勿用于任何商业用途!')
label_warning = tk.Label(frame_3, fg='blue', font=('楷体', 12), text='\n建议:将Chrome内核浏览器设置为默认浏览器\n作者:Jack_Cui')
# 控件布局
frame_1.pack()
frame_2.pack()
frame_3.pack()
group.grid(row=0, column=0)
tb1.grid(row=0, column=1)
tb2.grid(row=0, column=2)
label1.grid(row=0, column=0)
entry.grid(row=0, column=1)
label2.grid(row=0, column=2)
play.grid(row=0, column=3, ipadx=10, ipady=10)
label3.grid(row=0, column=4)
# download.grid(row = 0, column = 5,ipadx = 10, ipady = 10)
QR_Code.grid(row=0, column=0)
label_explain.grid(row=1, column=0)
label_warning.grid(row=2, column=0)
"""
函数说明:jsonp解析
Parameters:
_jsonp - jsonp字符串
Returns:
_json - json格式数据
Modify:
2017-05-11
"""
def loads_jsonp(self, _jsonp):
try:
_json = json.loads(re.match(".*?({.*}).*", _jsonp, re.S).group(1))
return _json
except:
raise ValueError('Invalid Input')
"""
函数说明:视频播放
"""
def video_play(self):
# 视频解析网站地址
port_1 = 'http://www.wmxz.wang/video.php?url='
port_2 = 'http://www.vipjiexi.com/tong.php?url='
# 正则表达是判定是否为合法链接
if re.match(r'^https?:/{2}\w.+$', self.url.get()):
if self.v.get() == 1:
# 视频链接获取
ip = self.url.get()
# 视频链接加密
ip = parse.quote_plus(ip)
# 浏览器打开
webbrowser.open(port_1 + self.url.get())
elif self.v.get() == 2:
# 链接获取
ip = self.url.get()
# 链接加密
ip = parse.quote_plus(ip)
# 获取time、key、url
get_url = 'http://www.vipjiexi.com/x2/tong.php?url=%s' % ip
# get_url_head = {
# 'User-Agent':'Mozilla/5.0 (Linux; Android 4.1.1; Nexus 7 Build/JRO03D) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.166 Safari/535.19',
# 'Referer':'http://www.vipjiexi.com/',
# }
# get_url_req = request.Request(url = get_url, headers = get_url_head)
# get_url_response = request.urlopen(get_url_req)
# get_url_html = get_url_response.read().decode('utf-8')
# bf = BeautifulSoup(get_url_html, 'lxml')
# a = str(bf.find_all('script'))
# pattern = re.compile('"api.php", {"time":"(\d+)", "key": "(.+)", "url": "(.+)","type"', re.IGNORECASE)
# string = pattern.findall(a)
# now_time = string[0][0]
# now_key = string[0][1]
# now_url = string[0][2]
# #请求播放,获取Success = 1
# get_movie_url = 'http://www.vipjiexi.com/x2/api.php'
# get_movie_data = {
# 'key':'%s' % now_key,
# 'time':'%s' % now_time,
# 'type':'',
# 'url':'%s' % now_url
# }
# get_movie_head = {
# 'User-Agent':'Mozilla/5.0 (Linux; Android 4.1.1; Nexus 7 Build/JRO03D) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.166 Safari/535.19',
# 'Referer':'http://www.vipjiexi.com/x2/tong.php?',
# 'url':'%s' % ip,
# }
# get_movie_req = request.Request(url = get_movie_url, headers = get_movie_head)
# get_movie_data = parse.urlencode(get_movie_data).encode('utf-8')
# get_movie_response = request.urlopen(get_movie_req, get_movie_data)
# 请求之后立刻打开
webbrowser.open(get_url)
else:
msgbox.showerror(title='错误', message='视频链接地址无效,请重新输入!')
"""
函数说明:视频下载,通过无名小站抓包(已经无法使用)
"""
def download_wmxz(self):
if re.match(r'^https?:/{2}\w.+$', self.url.get()):
# 视频链接获取
ip = self.url.get()
# 视频链接加密
ip = parse.quote_plus(ip)
# 获取保存视频的url
get_url = 'http://www.sfsft.com/index.php?url=%s' % ip
head = {
'User-Agent': 'Mozilla/5.0 (Linux; Android 4.1.1; Nexus 7 Build/JRO03D) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.166 Safari/535.19',
'Referer': 'http://www.sfsft.com/index.php?url=%s' % ip
}
get_url_req = request.Request(url=get_url, headers=head)
get_url_response = request.urlopen(get_url_req)
get_url_html = get_url_response.read().decode('utf-8')
bf = BeautifulSoup(get_url_html, 'lxml')
a = str(bf.find_all('script'))
pattern = re.compile("url : '(.+)',", re.IGNORECASE)
url = pattern.findall(a)[0]
# 获取视频地址
get_movie_url = 'http://www.sfsft.com/api.php'
get_movie_data = {
'up': '0',
'url': '%s' % url,
}
get_movie_req = request.Request(url=get_movie_url, headers=head)
get_movie_data = parse.urlencode(get_movie_data).encode('utf-8')
get_movie_response = request.urlopen(get_movie_req, get_movie_data)
get_movie_html = get_movie_response.read().decode('utf-8')
get_movie_data = json.loads(get_movie_html)
webbrowser.open(get_movie_data['url'])
else:
msgbox.showerror(title='错误', message='视频链接地址无效,请重新输入!')
"""
函数说明:生成二维码,手机观看
"""
def QR_Code(self):
if re.match(r'^https?:/{2}\w.+$', self.url.get()):
# 视频链接获取
ip = self.url.get()
# 视频链接加密
ip = parse.quote_plus(ip)
url = 'http://www.wmxz.wang/video.php?url=%s' % ip
words = url
images_pwd = os.getcwd() + '\Images\\'
png_path = images_pwd + 'bg.png'
qr_name = 'qrcode.png'
qr_path = images_pwd + 'qrcode.png'
run(words=words, picture=png_path, save_name=qr_name, save_dir=images_pwd)
top = tk.Toplevel(self.root)
img = tk.PhotoImage(file=qr_path)
text_label = tk.Label(top, fg='red', font=('楷体', 15), text="手机浏览器扫描二维码,在线观看视频!")
img_label = tk.Label(top, image=img)
text_label.pack()
img_label.pack()
top.mainloop()
else:
msgbox.showerror(title='错误', message='视频链接地址无效,请重新输入!')
"""
函数说明:tkinter窗口居中
"""
def center(self):
ws = self.root.winfo_screenwidth()
hs = self.root.winfo_screenheight()
x = int((ws / 2) - (self.w / 2))
y = int((hs / 2) - (self.h / 2))
self.root.geometry('{}x{}+{}+{}'.format(self.w, self.h, x, y))
"""
函数说明:loop等待用户事件
"""
def loop(self):
self.root.resizable(False, False) # 禁止修改窗口大小
self.center() # 窗口居中
self.root.mainloop()
if __name__ == '__main__':
app = APP() # 实例化APP对象
app.loop() # loop等待用户事件