最近研究项目需要用到一些资料,无奈网上资源要么添加无数广告花里胡哨,要么就是1页内容给你分了N页得来回点数次。研究的东西挺明确的,看文档给我整蒙了,为了让自己看着舒服及归纳总结,决定做个爬虫,由于Python的简便快捷以及多年之前也写过所以选定他来制作爬虫。
查找网上爬虫资料,发现写的大都千篇一律,要不就是贴段代码草草了事,对于小白那是看的稀里糊涂不得其法,对于大牛那更是不屑一顾根本看不上,每个网站不一样写的爬虫各有不同我这理所应当也没有啥太大帮助,当然我这白嫖的也没有权利说别人是吧,所以自己整吧,顺便分享给需要的朋友~
授之以鱼不如授之以渔,本篇主要写如何针对自己需求写出定制化爬虫~
一、Python安装
关于安装步骤,我觉得还是有必要写一写的,尝鲜小白很有可能因为一些基础问题而放弃,大神可以跳过,反正我是小白~
关心怎么写出定制化爬虫直接看第三节~
1. 下载安装文件
本人测试操作系统: Windows7 64位
Python官方地址: https://www.python.org
下载之后的安装包:
2. 安装及环境变量配置
Windows的好处安装直接下一步就好了,有路径需求自己选一下,其他也没什么好自定义的。。。
环境变量配置: 如下图所示
测试安装成功:
# 打开命令提示符(热键 + R 输入cmd 或者 随便找个地方按住Shift + 右键 选择 在此处打开命令窗口)进行测试
python -V
至此Python安装成功
二、Python集成开发环境
一个好的 IDE(Integrated Development Environment)可以让开发效率倍增,我这里有几个选择:系统自带记事本、notepad++、PyCharm、其他任意(VS Code、IDEA等)适用于不同情况
1. 系统自带记事本
适用于就是尝个鲜,看一下Python能干什么,我建议啥你也别装了,就系统自带记事本就好了,网上找几个实例粘贴到记事本,cmd命令提示符一运行,觉得挺好继续深入,觉得不满意果断弃坑,从入门到放弃~
# 命令提示符内直接键入命令即可运行
python script.py
2. notepad++
其实就是个加强版记事本,有些语法提示、高亮、行号等也是cmd命令提示符一运行就好,我刚开始就是用的它~
3. PyCharm
据说是一个比较好用的Python IDE,一些资料上也推荐使用PyCharm,当然我是没有使用的,因为我有其他IDE,所以也懒得装这么多开发环境了,官网找个图让大家看看界面~
4. VS Code、IDEA等
我用的是 IntelliJ IDEA 跟 PyCharm 一个公司出的,IDEA也支持Python,使用IDEA就是为了下断点方便调试,要不就一直记事本了~
三、爬虫定制化
前面是些基础建设,现在才是正文,主要讲需求转化为实际功能的思想,以及如何查看网站资源输出方式来确定py程序怎么写,授之以鱼不如授之以渔
1. 明确需求制定方案
首先你要知道自己想要的是什么,例如制作爬虫的目的是为了收集网站的文字、图片、视频等相关资源,当然可能还有其他目的但那不是本次讨论了~
比如我,制作爬虫是为将网站上好几页图文混排的资料下载整理成Markdown文件,过程中会有文字的整理、图片的下载及可能出现的视频资料。为了这个目的我需要做哪些准备?二方面,一是分析所需网站资源的输出方式;二是编写相应处理代码将资源解析整理并下载保存。
-
所需网站资源(目的): 文本、图片、视频
-
资源输出方式: HTML+CSS位置、GET+POST请求接口、图片地址、MP4或M3U8视频地址
-
处理模块分析: 解析HTML/XML、GET+POST请求、处理Json、图片下载、MP4下载、M3U8下载并整合
2. 分析资源输出方式
明确自己需求后就要分析实际网站资源的输出方式,因为各个网站不同,每个人需要的资源也不相同,就需要自己去发现去查找。
但网站不外乎就是静态和动态,自己想要的也不过是文字、图片、视频,下面我们随便找个网站进行试验,以实际问题进行逐步解说。
-
浏览器F12功能: 我们会用到浏览器开发人员工具功能(F12),浏览器用火狐、谷歌、360等都行,来查看我们需要的资源,以我用的360安全浏览器为例,来查询我想要的文字是通过什么方式获得最好。
-
Elements功能: F12之后,首先是Elements选项卡(图中①),先点击②再选择上方我需要的文字资源(图中③),下方HTML会发生变化,定位到所选资源位置(图中④,HTML可能为闭合状态,双击展开),右侧(图中⑤)为当前页面CSS定义,你修改的话(比如改个颜色、字号)页面也会跟随变化(F5刷新还原),依此可以定位需要内容在HTML中的位置。当然这只是静态网页,我们还有另外一种方式针对动态的比这更方便,点击Network选项卡(图中⑥)。
-
Network功能:
Network选项卡(图中①)可以方便查看你想要的资源,没有的话F5刷新下,通过类型筛选(图中②)可以筛选XHR(接口数据)、Img(图片资源)、Media(媒体资源)等等。在左侧(图中③)可看到被加载的资源有doc、css、js、jpg等,点击在右侧(图中④)显示详细信息,headers请求头、response响应结果、preview可以把json类型结果格式化并折叠方便查看,由于我这是个静态网站没有接口,我找个带接口的网站给大家看看。
从上图可以看到在某项里可能会有你需要的文字、图片地址、视频地址等信息,当然有些网站会有加密、权限认证、防外链等一系列措施阻止你轻易获取,但你只要坚信你能看到的就一定能获取得到就对了。。。
-
确定资源输出方式: 通过Elements或Network你可以找到你想要的资源,现在需要在Headers选项卡中找到资源获取的关键信息。
- Request URL: 请求URL,就是网页或接口的地址,没它你啥也做不了
- Request Method: 请求方式,一般有GET、POST等方式
- Request Headers: 这个参数就厉害了,一般的认证、防外链啥的都是拿它做的,比较重要的有Content-Type(请求参数的类型,一般有application/json、application/x-www-form-urlencoded等)、Cookie(这个一般存你的登录信息等,一些需要登录才能获取的资源是一定要有这项的)、Referer(这是发送请求的是哪个网站,一般用来防外链等,不是网站自己特定地址会被API网关拦截)、User-Agent (这项放的是你机子、浏览器版本等信息,可以区分你在哪通过什么方式访问的)等
- Request payload: 如果是GET请求一般参数是挂在地址栏一同请求,如果是POST就需要这项了,用来传递请求参数,比如你列表ID、查询条件等信息
3. 编写相应处理模块
之前说了那么多其实都是为了敲代码准备,你不明确自己想要的是什么,不知道资源的地址、参数、返回值结构,程序你根本没法写。
开搂代码前正确姿势:
不管程序写的咋样,代码结构还是要有一定规范的,写“主程序入口”及“处理方法”是为了项目化、组件化,如果以后用得上直接把这文件当成组件扔到到python项目里就好了。
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# 处理方法
def handler(parm1, parm2):
return parm1 + '+' + parm2
# 主程序入口
if __name__ == "__main__":
print('main')
print(handler('参数1', '参数2'))
爬虫常用库:
我们会用对HTTP进行请求,比较常见的库有urllib 、requests ;以及对HTML 和 XML 的解析,常见库有beautifulsoup 、pyquery 、lxml ,你想装几个就装几个,全都装了也是可以的。
# 以pip方式进行安装,方便快捷
# pip install 包名
pip install requests pyquery
功能模块规划:
我这次其实仅从接口获取数据也不需要样式,按自己格式拼接后,过滤数据里的HTML标记,替换IMG标记为MD文件特有标记,保存至Markdown文件,先编写个大致架构,有其他需求也可按我这思路进行拓展。
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
import os
import time
# 发送http请求,返回json数据
def http(url, header, payload):
return 'json数据'
# 创建文件夹,返回文件夹路径
def mkdir(path, index, name):
return '文件夹路径'
# 创建md文件,返回文件路径
def mk_file(path, index, name, content):
return '文件路径'
# html格式化,去除<p><br>等标签并将<img>替换为Markdown的![]()写法,返回格式化后内容
def html_format(html_str):
return '格式化后内容'
# 处理img标签转为md格式
def img2md(matched):
return '![]()格式'
# 生成图片名称
def img_name(url):
return '图片路径'
# 下载图片
def down_img(url, name):
return '下载成功'
# 处理方法
def handler():
# 记录循环编号
i1 = 0
# 请求接口获得json数据
j1 = http(urls[0], headers[0], payloads[0])
# 判断文章列表数据
if j1 and j1['articleList']:
# 创建文件夹
path = mkdir(path0, i1, 'folderName')
for article in j1['articleList']:
# 文章内容格式化
content = html_format(article['content'])
# 创建Markdown文件
path = mk_file(path, i1, article['titleName'], content)
# 编号递增
i1 += 1
# 定义全局变量
path0 = os.getcwd() # 获取py文件目录
img_i = 1 # 图片编号初始化
img_pre1 = '../../../images/' # 图片路径
img_pre2 = time.strftime("%Y%m%d", time.localtime()) # 图片文件前缀为当前日期
# API请求地址
urls = [
'http://Interface1',
'http://Interface2',
'http://Interface3'
]
# 请求头数据
headers = [
{
'Content-Type': 'application/json',
'Cookie': 'uid=123456789',
'Referer': 'http://xxxxxxxxx',
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) Chrome/78.0.3904.108 Safari/537.36',
}
]
# API请求参数
payloads = [
{"mid": 123},
{"mid": 123, "lid": 456},
{"mid": 123, "lid": 456, "sid": 789}
]
# 主程序入口
if __name__ == "__main__":
print('main')
handler()
按照大致框架去填充具体代码:
我爬取的网站购买的许可仅可以自己查看,由于版权问题我并不能公开分享,所以这里贴一些关键功能模块,一些网站业务流程之前也说过怎么去查看去分析,每个网站也各不相同需要自行研究。
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
import html
import json
import os
import re
import time
import requests
# 发送http请求,返回json数据
def http(url, header, payload):
# 发送http请求
r = requests.post(url, headers=header, data=json.dumps(payload))
# 获取json数据
j = r.json()
# 判断是否成功
if j and j['result'] == 1:
return j['resultData']
# 创建文件夹,返回文件夹路径
def mkdir(path, index, name):
path += '\\' + '%02d' % index + '.' + name
exists = os.path.exists(path)
if not exists:
os.makedirs(path)
print(path + ' 创建成功')
return path
# 创建md文件,返回文件路径
def mk_file(path, index, name, content):
path += '\\' + '%02d' % index + '.' + name + '.md'
exists = os.path.isfile(path)
if not exists:
f = open(path, 'w+', encoding='utf-8')
f.write(content)
f.close()
print(path + ' 创建成功')
return path
# html格式化,去除<p><br>等标签并将<img>替换为Markdown的![]()写法,返回格式化后内容
def html_format(html_str):
# 去除无用html标签
html_str = re.sub('<p[^>]*>|</p>|<span[^>]*>|</span>|<br>', '', html.unescape(html_str))
# 将<img>替换为Markdown的![]()写法
return re.sub('<img src="(?P<url>[^"|^>]+)"[^>]+>', img2md, html_str)
# 处理img标签转为md格式
def img2md(matched):
# 获取原始url
url = matched.group('url')
return ' + ')'
# 生成图片名称
def img_name(url):
global img_i
suffix = os.path.splitext(url)[1]
name = img_pre2 + '%02d' % img_i + suffix
# 下载图片
down_img(url, name)
img_i += 1
return img_pre1 + name
# 下载图片
def down_img(url, name):
path = path0 + '\\images'
exists = os.path.exists(path)
if not exists:
os.makedirs(path)
print(path + ' 创建成功')
filename = path + '\\' + name
try:
r = requests.get(url, timeout=10)
if r.status_code == 200:
with open(filename, "wb") as f:
f.write(r.content)
except requests.exceptions.ConnectionError:
print(filename + '下载失败')
# 处理方法
def handler():
# 记录循环编号
i1 = 0
# 请求接口获得json数据
j1 = http(urls[0], headers[0], payloads[0])
# 判断文章列表数据
if j1 and j1['articleList']:
# 创建文件夹
path = mkdir(path0, i1, 'folderName')
for article in j1['articleList']:
# 文章内容格式化
content = html_format(article['content'])
# 创建Markdown文件
mk_file(path, i1, article['titleName'], content)
# 编号递增
i1 += 1
# 定义全局变量
path0 = os.getcwd() # 获取py文件目录
img_i = 1 # 图片编号初始化
img_pre1 = '../../../images/' # 图片路径
img_pre2 = time.strftime("%Y%m%d", time.localtime()) # 图片文件前缀为当前日期
# API请求地址
urls = [
'http://Interface1',
'http://Interface2',
'http://Interface3'
]
# 请求头数据
headers = [
{
'Content-Type': 'application/json',
'Cookie': 'uid=123456789',
'Referer': 'http://xxxxxxxxx',
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) Chrome/78.0.3904.108 Safari/537.36',
}
]
# API请求参数
payloads = [
{"mid": 123},
{"mid": 123, "lid": 456},
{"mid": 123, "lid": 456, "sid": 789}
]
# 主程序入口
if __name__ == "__main__":
print('main')
handler()
进行代码调试:
其实如果代码可以下断点调试,处理完技术难点剩下就是时间了。我简单介绍下IDEA是怎么下断点进行调试的,其他 IDE 也都有这方便功能。
-
点击行号右侧可以下断点,再次点击取消
-
点击绿色三角或右键代码 选Debug进行调试
-
下图①位置控制重启、调至下一断点(F9)、关闭,图②位置控制跳入(F7)、跳过(F8),图③可以监控变量
关于视频下载:
MP4格式就跟图片差不多不细说了,这里说下M3U8格式的,现在大多数在线视频网站为了视频流畅度让用户更好地体验等因素都选择把整个流分块用一个M3U8清单记录所有视频。你在F12的Network中可以发现M3U8文件地址,下载后是个文件清单,将上面每个文件进行下载后在合并就可以获得完整视频,当然有的网站做的比较隐蔽,你需要耐心去查找。
至此一个python开发环境,爬虫设计思想,以及编码调试过程都简要的说了一遍,编程是门艺术,剩下就到你发挥艺术想象力的时候了,授之以鱼不如授之以渔~