爬虫案例

爬虫小案例

一、猫眼电影

需求:

  • 电影名称
  • 主演
  • 上映时间

操作步骤:

  • 查看是否为动态加载

    非动态加载

  • 找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存储:

SQL建库建表

源码连接

做一个简单的查询:

  • 查询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))
    • salt:

      • JavaScript:ts + parseInt(10 * Math.random(), 10);
      • Python:ts + str(random.randint(0, 9))
    • 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()

实现步骤:

posted @ 2019-09-17 09:06  ChanceySolo  阅读(299)  评论(0编辑  收藏  举报