爬虫案例
爬虫小案例
一、猫眼电影
需求:
- 电影名称
- 主演
- 上映时间
操作步骤:
-
查看是否为动态加载
非动态加载
-
找URL规律
https://maoyan.com/board/4?offset=0
第一页https://maoyan.com/board/4?offset=10
第二页 -
正则表达式
<div class="movie-item-info">.*?title="(.*?)".*?class="star">(.*?)</p>.*?releasetime">(.*?)</p>
-
xpath表达式
name_list = ["", "", ""]
star_list = ["", "", ""]
time_list = ["", "", ""]
1. urllib+re
源码如下:
如果对以上运行时间进行优化
- 可以采用csv一次性多行写入,即以元组的形式
- 取消sleep,可能导致封IP,考虑代理IP
- 多线程
使用MySQL存储:
做一个简单的查询:
-
查询20年以前的电影的名字和上映时间
select name,time from filmtab where time < (now() - interval 20 year);
-
查询1990-2000年的电影的名字和上映时间
select name,time from filmtab where time >= "1990-01-01" and time <= "2000-01-01";
2. requests+xpath
二、电影天堂
需求:
*****************一级页面*****************
1、电影名称
2、电影链接
*****************一级页面*****************
1、下载链接
操作步骤:
-
查看是否为静态页面
-
找URL规律
https://www.dytt8.net/html/gndy/dyzz/list_23_1.html
第一页https://www.dytt8.net/html/gndy/dyzz/list_23_2.html第二页
https://www.dytt8.net/html/gndy/dyzz/list_23_3.html
第三页 -
正则表达式
-
一级
<b>.*?href="(.*?)" class="ulink">(.*?)</a>.*?</b>
-
二级
<td style="WORD-WRAP: break-word" bgcolor="#fdfddf"><a.*?(.*?)</a></td>
-
-
存进数据库(mysql)
在网页解码的时候,会因为有部分无关紧要的信息无法解码而频繁报错,则可以在网页解码的时候添加参数
decode("gbk", "ignore")
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for moviesky
-- ----------------------------
DROP TABLE IF EXISTS `moviesky`;
CREATE TABLE `moviesky` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
`link` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 46 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
SET FOREIGN_KEY_CHECKS = 1;
三、链家
需求:
- 房源名称
- 总价
- 单价
- 详情
分析:
-
基准表达式(匹配每个房源信息的节点列表)
//ul[@class="sellListContent"]/li
-
详情表达式
房源名称:
.//a[@class="no_resblock_a"]/text()
总价:
./div/div[@class="priceInfo"]/div[@class="totalPrice"]/span/text()
单价:
./div/div[@class="priceInfo"]/div[2]/span/text()
详情:
.//div[@class="houseInfo"]/text()
源码:
四、百度贴吧
以伟大的慈善家、性感女神泰勒.斯威夫特为例
需求:
- 抓取指定贴吧所有的图片
思路:
- 获取贴吧主页URL,下一页,找到不同页URL规律
- 获取第一页所有帖子URL地址:[帖子链接1,帖子链接2,...]
- 对每个帖子链接发请求,获取图片URL
- 请求图片,保存本地
xpath表达式:
- 帖子链接:
//*[@id="thread_list"]/li//div[@class="t_con cleafix"]/div/div/div/a/@href
- 图片链接:
//div[@class="d_post_content_main d_post_content_firstfloor"]//div[@class="d_post_content j_d_post_content clearfix"]/img[@class="BDE_Image"]/@src
附加:
- 帖子标题(二级页面):
//h1[@class="core_title_txt"]/text()
- 帖子发布者(二级页面):
//*[@id="j_p_postlist"]/div[1]/div[2]/ul/li[3]/a/text()
五、糗事百科
需求:
- 用户昵称
- 段子内容
- 好笑数量
- 评论数量
xpath语法:
- 基准列表
//div[@id="content-left"]/div
- 详细信息
- 用户昵称:
.//h2/text()
- 段子内容:
.//div[@class="content"]/span/text()
- 好笑数量:
./div/span/i/text()
- 评论数量:
./div/span/a/i/text()
- 用户昵称:
翻页:
https://www.qiushibaike.com/hot/page/1/
https://www.qiushibaike.com/hot/page/2/
源码:
六、翻译
1. 有道翻译
该项目为JS加密+异步加载
需求:
- 自动翻译用户输入的要翻译的词汇
分析过程:
-
异步加载:
http://fanyi.youdao.com/translate_o?smartresult=dict&smartresult=rule
查询参数:
i: tiger # 查询的词汇 from: AUTO to: AUTO smartresult: dict client: fanyideskweb salt: 15678362639662 # JS加密后的字段 sign: 75ef5f00f335caa2abddde0cb4265724 # JS加密后的字段 ts: 1567836263966 # JS加密后的字段 bv: 7e3150ecbdf9de52dc355751b074cf60 doctype: json version: 2.1 keyfrom: fanyi.web action: FY_BY_REALTlME
-
分析JS加密过程(断点调试)
-
ts : 13的时间戳
- JavaScript:
""+ (new Data).getTime()
- Python:
str(int(time.time() * 1000))
- JavaScript:
-
salt:
- JavaScript:
ts + parseInt(10 * Math.random(), 10);
- Python:
ts + str(random.randint(0, 9))
- JavaScript:
-
sign(断点调试之后发现变零e为要翻译的词汇)
-
JavaScript:
n.md5("fanyideskweb" + e + salt + "n%A-rKaT5fb[Gy?;N5@Tj")
-
Python
from bashlib import md5 s = md5() s.update("fanyixxxx".encode()) sign = s.hexdigest()
-
-
源码:
2. 百度翻译
请求的地址:https://fanyi.baidu.com/v2transapi
请求的方式:POST
通过案例六的分析流程,得到如下的formdata
from: en
to: zh
query: cat
transtype: realtime
simple_means_flag: 3
sign: 661701.982004
token: b1c15abb7f12da19b720303e59ae9e93
参数作用:
form...to..
将什么翻译成什么- query:要翻译的词汇
- transtype : 不清楚,不过换单词也不会变,直接写死
- simple_means_flag : 不清楚
- sign : js加密后的字串
- token : js加密后的字串
分析加密:
-
sign加密过程(python实现难度较大)
function a(r) { if (Array.isArray(r)) { for (var o = 0, t = Array(r.length); o < r.length; o++) t[o] = r[o]; return t } return Array.from(r) } function n(r, o) { for (var t = 0; t < o.length - 2; t += 3) { var a = o.charAt(t + 2); a = a >= "a" ? a.charCodeAt(0) - 87 : Number(a), a = "+" === o.charAt(t + 1) ? r >>> a : r << a, r = "+" === o.charAt(t) ? r + a & 4294967295 : r ^ a } return r } function e(r) { var o = r.match(/[\uD800-\uDBFF][\uDC00-\uDFFF]/g); if (null === o) { var t = r.length; t > 30 && (r = "" + r.substr(0, 10) + r.substr(Math.floor(t / 2) - 5, 10) + r.substr(-10, 10)) } else { for (var e = r.split(/[\uD800-\uDBFF][\uDC00-\uDFFF]/), C = 0, h = e.length, f = []; h > C; C++) "" !== e[C] && f.push.apply(f, a(e[C].split(""))), C !== h - 1 && f.push(o[C]); var g = f.length; g > 30 && (r = f.slice(0, 10).join("") + f.slice(Math.floor(g / 2) - 5, Math.floor(g / 2) + 5).join("") + f.slice(-10).join("")) } var u = "320305.131321201" // var u = void 0 // , l = "" + String.fromCharCode(103) + //String.fromCharCode(116) + String.fromCharCode(107); // u = null !== i ? i : (i = window[l] || "") || ""; for (var d = u.split("."), m = Number(d[0]) || 0, s = Number(d[1]) || 0, S = [], c = 0, v = 0; v < r.length; v++) { var A = r.charCodeAt(v); 128 > A ? S[c++] = A : (2048 > A ? S[c++] = A >> 6 | 192 : (55296 === (64512 & A) && v + 1 < r.length && 56320 === (64512 & r.charCodeAt(v + 1)) ? (A = 65536 + ((1023 & A) << 10) + (1023 & r.charCodeAt(++v)), S[c++] = A >> 18 | 240, S[c++] = A >> 12 & 63 | 128) : S[c++] = A >> 12 | 224, S[c++] = A >> 6 & 63 | 128), S[c++] = 63 & A | 128) } for (var p = m, F = "" + String.fromCharCode(43) + String.fromCharCode(45) + String.fromCharCode(97) + ("" + String.fromCharCode(94) + String.fromCharCode(43) + String.fromCharCode(54)), D = "" + String.fromCharCode(43) + String.fromCharCode(45) + String.fromCharCode(51) + ("" + String.fromCharCode(94) + String.fromCharCode(43) + String.fromCharCode(98)) + ("" + String.fromCharCode(43) + String.fromCharCode(45) + String.fromCharCode(102)), b = 0; b < S.length; b++) p += S[b], p = n(p, F); return p = n(p, D), p ^= s, 0 > p && (p = (2147483647 & p) + 2147483648), p %= 1e6, p.toString() + "." + (p ^ m) } });
-
token 加密过程
# 在首页源码中会发现 token: 'b1c15abb7f12da19b720303e59ae9e93', # URL https://fanyi.baidu.com/
七、豆瓣电影
需求:
- 地址: 豆瓣电影 - 排行榜 - 剧情
- 目标: 电影名称、电影评分
F12抓包(XHR):
-
Request URL(基准URL地址) :
https://movie.douban.com/j/chart/top_list?
-
Query String(查询参数)
抓取的查询参数如下:
type: 13
interval_id: 100:90
action: ''
start: 0
limit: 用户输入的电影数量
JSON模块的使用:
-
json.loads ( json格式的字符串 )
:把json格式的字符串转为python数据类型示例
html = json.loads(res.text)
print(type(html))
源码:
八、腾讯招聘
需求:
- 岗位名称
- 岗位城市
- 岗位职责
- 岗位要求
分析过程:
-
基准URL:
https://careers.tencent.com/search.html
-
通过抓包不难发现,Ajax异步加载
-
职位列表API:
https://careers.tencent.com/tencentcareer/api/post/Query?pageIndex={}&pageSize={}
其中,pageIndex为页码,pageSize为职位数量
-
职位详情API:
https://careers.tencent.com/tencentcareer/api/post/ByPostId?&postId={}}
postID可在职位列表API中得到
-
源码:
九、人人网模拟登陆
法一、携带cookie
通过抓包,分析cookie
在发送get请求的时候直接携带cookie
源码:
法二、发送POST
使用requests中的session做会话保持
源码:
十、区号数据
1. v 1.0
URL:http://www.mca.gov.cn/article/sj/xzqh/2019/
需求:
- 获取最新的国县以上的行政区代码
分析过程:
- 在一级页面中拿到的连接是假连接,无法正常跳转二级页面
- 分析二级页面,找真正的URL
2. v 2.0
升级点:
- 将爬到的数据按照层级关系存储,以此来优化查询速度
分析瓶颈:
- 建表,分三张表,省、市、县
- 写入数据用
executemany()
,提升爬虫速度
思路:
- selenium+Chrome打开一级页面,并提取二级页面最新链接
- 增量爬取: 和数据库version表中进行比对,确定之前是否爬过(是否有更新)
- 如果没有更新,直接提示用户,无须继续爬取
- 如果有更新,则删除之前表中数据,重新爬取并插入数据库表
- 最终完成后: 断开数据库连接,关闭浏览器
源码:
SQL练习:
-
查询所有省市县信息(多表查询)
-
查询所有省市县信息(连接查询)
select province.p_name,city.c_name,county.x_name from province inner join city on province.p_code=city.c_father_code inner join county on city.c_code=county.x_father_code;
十一、京东爬取
目标:
- 目标网址 :https://www.jd.com/
- 抓取目标 :商品名称、商品价格、评价数量、商品商家
思路:
- 打开京东,到商品搜索页
- 匹配所有商品节点对象列表
- 把节点对象的文本内容取出来,查看规律
- 提取完1页后,判断如果不是最后1页,则点击下一页
实现步骤:
- 首页搜索框 :
//*[@id="key"]
* - *首页搜索按钮 😕/*[@id="search"]/div/div[2]/button
- 商品页的 商品信息节点对象列表 😕/*[@id="J_goodsList"]/ul/li
执行JS脚本,获取动态加载数据:
- browser.execute_script(
'window.scrollTo(0,document.body.scrollHeight)'
)
代码实现:
十二、盗墓笔记
目标:
- url:
http://www.daomubiji.com/
- 抓取目标网站中盗墓笔记1-8中所有章节的所有小说的具体内容,保存到本地文件
分析:
- 三级页面,首先考虑封装请求函数
xpath:
-
一级页面的集链接:
//article[@class="article-content"]/a/@href
集名称:
//article[@class="article-content"]/a//h2/text()
-
二级页面
基准xpath:
//article
章节链接:
./a/@href
章节名:
./a/text()
-
三级页面文章内容:
//article[@class="article-content"]//p/text()
实现步骤: