爬虫基础以及一个简单的实例(requests,re)
最近在看爬虫方面的知识,看到崔庆才所著的《Python3网络爬虫开发实战》一书讲的比较系统,果断入手学习。下面根据书中的内容,简单总结一下爬虫的基础知识,并且实际练习一下。详细内容请见:https://cuiqingcai.com/5465.html(作者已把书的前几章内容对外公开)。
在写爬虫程序之前需要了解的一些知识:
爬虫基础:我们平时访问网页就是对服务器发送请求(Request),然后得到响应(Response)的一个过程。爬虫通过模仿浏览器,对网页进行自动访问。需要知道请求包含哪些内容,请求的方式有哪些,响应包含哪些内容。
网页结构:网页由HTML,CSS,JaveScript组成。需要知道其各自的作用是什么,还需要知道到哪个节点去获取自己想要的信息。
其他:了解会话(Session),Cookie,代理(Proxy)的作用。
爬虫流程:
- 爬取网页(获取网页源代码):可使用的库有urllib,requests等;当然,现在很多网页都是动态加载的,对于这些网页,还需使用Selenium等库
- 解析网页(提取网页中我们需要的信息):定位信息的方式有:正则表达式,XPath选择器,CSS选择器;可使用的库有re,lxml,Beautiful Soup等
- 保存结果(将结果保存至文件或数据库):文件有txt,json, csv等格式;数据库可选择MySQL,MongoDB等
在python中爬取网页,我们一般用requests库。下面是经常用到的一些语法:
导入requests库: import requests
获取响应: response=requests.get(url, headers)
获取响应体: response.text
下面让我们来实际操练一下:
实例目标:用requests库爬取猫眼电影网上top100的电影(排名,图片,电影名称,上映时间,评分),用正则表达式进行解析,然后将结果保存至txt文件
实例网址:https://maoyan.com/board/4
首先,导入requests库和re,json模块:
- import requests
- import re
- import json
其次,先定义一个爬取一个网页的方法:
- def get_one_page(url):
- headers={'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) \
- AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.162 Safari/537.36'}
- response=requests.get(url,headers=headers)
- if response.status_code==200:
- return response.text
- return None
这样,在main()方法里,我们设定好url,就可以把该网页源代码打印出来:
- def main():
- url="https://maoyan.com/board/4"
- html=get_one_page(url)
- print(html)
接下来,我们来仔细查看这个源代码,看看怎样用正则表达式把我们需要的信息提取出来。首先用浏览器打开这个网页,然后在浏览器里面选择开发者工具,在Network里查看网页源代码。下面截取一部分:
- <div class="content">
- <div class="wrapper">
- <div class="main">
- <p class="update-time">2018-12-30<span class="has-fresh-text">已更新</span></p>
- <p class="board-content">榜单规则:将猫眼电影库中的经典影片,按照评分和评分人数从高到低综合排序取前100名,每天上午10点更新。相关数据来源于“猫眼电影库”。</p>
- <dl class="board-wrapper">
- <dd>
- <i class="board-index board-index-1">1</i>
- <a href="/films/1203" title="霸王别姬" class="image-link" data-act="boarditem-click" data-val="{movieId:1203}">
- <img src="//ms0.meituan.net/mywww/image/loading_2.e3d934bf.png" alt="" class="poster-default" />
- <img data-src="https://p1.meituan.net/movie/20803f59291c47e1e116c11963ce019e68711.jpg@160w_220h_1e_1c" alt="霸王别姬" class="board-img" />
- </a>
- <div class="board-item-main">
- <div class="board-item-content">
- <div class="movie-item-info">
- <p class="name"><a href="/films/1203" title="霸王别姬" data-act="boarditem-click" data-val="{movieId:1203}">霸王别姬</a></p>
- <p class="star">
- 主演:张国荣,张丰毅,巩俐
- </p>
- <p class="releasetime">上映时间:1993-01-01</p> </div>
- <div class="movie-item-number score-num">
- <p class="score"><i class="integer">9.</i><i class="fraction">6</i></p>
可以看到,电影的排名在一个dd节点下面:
- <dd>
- <i class="board-index board-index-1">1</i>
因此,相应的正则表达式可以写为:<dd>.*?board-index.*?>(.*?)</i>
接下来,我们发现图片在一个a节点下面,但是有两张图片。经过检查,第二个img节点下的data-src属性是图片的链接:
- <img data-src="https://p1.meituan.net/movie/20803f59291c47e1e116c11963ce019e68711.jpg@160w_220h_1e_1c" alt="霸王别姬" class="board-img" />
因此,相应的正则表达式可以写为:.*?data-src="(.*?)" (注:因为这个会接在之前的正则表达式之后,因此最前面写上.*?即可。下同。)
再接下来,电影的名称,在一个p节点下面,class为"name":
- <p class="name"><a href="/films/1203" title="霸王别姬" data-act="boarditem-click" data-val="{movieId:1203}">霸王别姬</a></p>
相应的正则表达式可以写为:.*?name.*?a.*?>(.*?)</a>
上映时间,在一个p节点下面,class为"releasetime":
- <p class="releasetime">上映时间:1993-01-01</p>
相应的正则表达式可以写为:.*?releasetime.*?>(.*?)</p>
评分,在一个p节点下面,class为"score":
- <p class="score"><i class="integer">9.</i><i class="fraction">6</i></p>
相应的正则表达式可以写为:.*?score.*?integer">(.*?)</i>.*?fraction">(.*?)</i>.*?</dd> (注:最后用dd节点收尾)
把这些正则表达式连接起来,然后就可以用findall()方法查找出所有符合条件的内容。完整的正则表达式如下:
- <dd>.*?board-index.*?>(.*?)</i>.*?data-src="(.*?)".*?name.*?a.*?>(.*?)</a>.*?releasetime.*?>(.*?)</p>.*?score.*?integer">(.*?)</i>.*?fraction">(.*?)</i>.*?</dd>
下面,我们再定义一个解析网页的方法:
- def parse_one_page(html):
- pattern=re.compile('<dd>.*?board-index.*?>(.*?)</i>.*?data-src="(.*?)".*?name.*?a.*?>(.*?)</a>.*?releasetime.*?>(.*?)</p>.*?score.*?integer">(.*?)</i>.*?fraction">(.*?)</i>.*?</dd>', re.S)
- result=re.findall(pattern, html)
- return result
这里需要注意,在定义正则表达式的pattern时,必须加上re.S修饰符(匹配包括换行符在内的所有字符),否则碰到换行就无法进行匹配。
输出的匹配结果如下:
- [('', 'https://p1.meituan.net/movie/20803f59291c47e1e116c11963ce019e68711.jpg@160w_220h_1e_1c', '霸王别姬', '上映时间:1993-01-01', '9.', ''), ('', 'https://p0.meituan.net/movie/283292171619cdfd5b240c8fd093f1eb255670.jpg@160w_220h_1e_1c', '肖申克的救赎', '上映时间:1994-10-14(美国)', '9.', ''), ('', 'https://p0.meituan.net/movie/54617769d96807e4d81804284ffe2a27239007.jpg@160w_220h_1e_1c', '罗马假日', '上映时间:1953-09-02(美国)', '9.', ''), ('', 'https://p0.meituan.net/movie/e55ec5d18ccc83ba7db68caae54f165f95924.jpg@160w_220h_1e_1c', '这个杀手不太冷', '上映时间:1994-09-14(法国)', '9.', ''), ('', 'https://p1.meituan.net/movie/f5a924f362f050881f2b8f82e852747c118515.jpg@160w_220h_1e_1c', '教父', '上映时间:1972-03-24(美国)', '9.', ''), ('', 'https://p1.meituan.net/movie/0699ac97c82cf01638aa5023562d6134351277.jpg@160w_220h_1e_1c', '泰坦尼克号', '上映时间:1998-04-03', '9.', ''), ('', 'https://p0.meituan.net/movie/da64660f82b98cdc1b8a3804e69609e041108.jpg@160w_220h_1e_1c', '唐伯虎点秋香', '上映时间:1993-07-01(中国香港)', '9.', ''), ('', 'https://p0.meituan.net/movie/b076ce63e9860ecf1ee9839badee5228329384.jpg@160w_220h_1e_1c', '千与千寻', '上映时间:2001-07-20(日本)', '9.', ''), ('', 'https://p0.meituan.net/movie/46c29a8b8d8424bdda7715e6fd779c66235684.jpg@160w_220h_1e_1c', '魂断蓝桥', '上映时间:1940-05-17(美国)', '9.', ''), ('', 'https://p0.meituan.net/movie/230e71d398e0c54730d58dc4bb6e4cca51662.jpg@160w_220h_1e_1c', '乱世佳人', '上映时间:1939-12-15(美国)', '9.', '')]
可以看出,上述的格式还是有些杂乱,让我们修改一下解析网页的方法,使其变为整齐的结构化数据:
- def parse_one_page(html):
- pattern=re.compile('<dd>.*?board-index.*?>(.*?)</i>.*?data-src="(.*?)".*?name.*?a.*?>(.*?)</a>.*?releasetime.*?>(.*?)</p>.*?score.*?integer">(.*?)</i>.*?fraction">(.*?)</i>.*?</dd>', re.S)
- result=re.findall(pattern, html)
- for item in result:
- yield {"index": item[0], "movie_name": item[2],\
- "pic": item[1], "release": item[3],\
- "score": item[4]+item[5]}
现在匹配结果变成了字典格式:
- {'index': '', 'movie_name': '霸王别姬', 'pic': 'https://p1.meituan.net/movie/20803f59291c47e1e116c11963ce019e68711.jpg@160w_220h_1e_1c', 'release': '上映时间:1993-01-01', 'score': '9.6'}
- {'index': '', 'movie_name': '肖申克的救赎', 'pic': 'https://p0.meituan.net/movie/283292171619cdfd5b240c8fd093f1eb255670.jpg@160w_220h_1e_1c', 'release': '上映时间:1994-10-14(美国)', 'score': '9.5'}
- {'index': '', 'movie_name': '罗马假日', 'pic': 'https://p0.meituan.net/movie/54617769d96807e4d81804284ffe2a27239007.jpg@160w_220h_1e_1c', 'release': '上映时间:1953-09-02(美国)', 'score': '9.1'}
- {'index': '', 'movie_name': '这个杀手不太冷', 'pic': 'https://p0.meituan.net/movie/e55ec5d18ccc83ba7db68caae54f165f95924.jpg@160w_220h_1e_1c', 'release': '上映时间:1994-09-14(法国)', 'score': '9.5'}
- {'index': '', 'movie_name': '教父', 'pic': 'https://p1.meituan.net/movie/f5a924f362f050881f2b8f82e852747c118515.jpg@160w_220h_1e_1c', 'release': '上映时间:1972-03-24(美国)', 'score': '9.3'}
- {'index': '', 'movie_name': '泰坦尼克号', 'pic': 'https://p1.meituan.net/movie/0699ac97c82cf01638aa5023562d6134351277.jpg@160w_220h_1e_1c', 'release': '上映时间:1998-04-03', 'score': '9.5'}
- {'index': '', 'movie_name': '唐伯虎点秋香', 'pic': 'https://p0.meituan.net/movie/da64660f82b98cdc1b8a3804e69609e041108.jpg@160w_220h_1e_1c', 'release': '上映时间:1993-07-01(中国香港)', 'score': '9.2'}
- {'index': '', 'movie_name': '千与千寻', 'pic': 'https://p0.meituan.net/movie/b076ce63e9860ecf1ee9839badee5228329384.jpg@160w_220h_1e_1c', 'release': '上映时间:2001-07-20(日本)', 'score': '9.3'}
- {'index': '', 'movie_name': '魂断蓝桥', 'pic': 'https://p0.meituan.net/movie/46c29a8b8d8424bdda7715e6fd779c66235684.jpg@160w_220h_1e_1c', 'release': '上映时间:1940-05-17(美国)', 'score': '9.2'}
- {'index': '', 'movie_name': '乱世佳人', 'pic': 'https://p0.meituan.net/movie/230e71d398e0c54730d58dc4bb6e4cca51662.jpg@160w_220h_1e_1c', 'release': '上映时间:1939-12-15(美国)', 'score': '9.1'}
接下来要将结果写入txt文件,这里定义一个写入文件的方法:
- def write_to_file(result):
- with open ("result.txt","a") as f:
- f.write(json.dumps(result, ensure_ascii=False)+'\n')
然后在main方法里将结果逐行写入文件:
- def main():
- url="https://maoyan.com/board/4"
- html=get_one_page(url)
- result=parse_one_page(html)
- for i in result:
- write_to_file(i)
这里有几个需要注意的地方:1,由于需要将结果逐行写入,因此文件用"a"方式打开,a也就是append。
2,由于需要将结果逐行写入,因此将结果写入文件时最后加上换行符"\n"。
3,由于结果是字典格式,无法直接写入文件,需要先用json.dumps方法把字典转为字符串,但是这样会导致中文乱码。根据json.dumps方法的注释,如果将ensure_ascii设为false,那么写入的字符串可以包含非ASCII字符,否则,所有这些字符都会在JSON字符串中转义。也就是说将参数ensure_ascii设为False可以使中文(UTF-8编码)不经过转义,也就不会乱码。
至此,第一页网页就已经全部爬取成功了。但是一共有10页这样的网页,我们打开第二个网页和第三个网页看一下。可以发现,第二个网页的url变为:https://maoyan.com/board/4?offset=10,第三页网页的url则是:https://maoyan.com/board/4?offset=20。可以发现规律就是多了一个offset参数,那么我们把1~10页的网页爬取url设置从offset为0,一直到offset为90,就可以爬取所有网页了。
由于我们在main方法里设定了爬取url,因此我们给main方法增加一个输入参数,也就是offset偏移值,这样,我们就能爬取我们想要的网页了。最后,再增添一个循环语句,用于爬取各种offset的网页,这样,一个简单的爬虫程序就完成了。
我们再把代码重新整合一下,并且由于现在猫眼多了反爬虫,如果爬取速度过快,会没有响应,因此,需要加上一个延时。
完整代码如下:
- import requests
- import re
- import json
- import time
- def get_one_page(url):
- try:
- headers={'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) \
- AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.162 Safari/537.36'}
- response=requests.get(url, headers=headers)
- if response.status_code==200:
- return response.text
- return None
- except requests.RequestException:
- print("Fail")
- def parse_one_page(html):
- pattern=re.compile('<dd>.*?board-index.*?>(.*?)</i>.*?data-src="(.*?)".*?name.*?a.*?>(.*?)</a>.*?releasetime.*?>(.*?)</p>.*?score.*?integer">(.*?)</i>.*?fraction">(.*?)</i>.*?</dd>', re.S)
- result=re.findall(pattern, html)
- for item in result:
- yield {"index": item[0], "movie_name": item[2],\
- "pic": item[1], "release": item[3],\
- "score": item[4]+item[5]}
- def write_to_file(result):
- with open ("result.txt","a") as f:
- f.write(json.dumps(result, ensure_ascii=False)+'\n')
- def main(offset):
- url="https://maoyan.com/board/4?offset={}".format(offset)
- html=get_one_page(url)
- result=parse_one_page(html)
- for i in result:
- write_to_file(i)
- if __name__=='__main__':
- for i in range(10):
- main(offset=i*10)
- time.sleep(1)
爬虫基础以及一个简单的实例(requests,re)的更多相关文章
- JMeter基础之一 一个简单的性能测试
JMeter基础之一 一个简单的性能测试 上一节中,我们了解了jmeter的一此主要元件,那么这些元件如何使用到性能测试中呢.这一节创建一个简单的测试计划来使用这些元件.该计划对应的测试需求. 1)测 ...
- C语言入门教程: 一个简单的实例
对于学习要保持敬畏! 语言不只是一种工具,还是一种资源,因此,善待它,掌握它! 我们知道,对于未知通常都会充满好奇和畏惧,既想了解它,又害怕神秘面纱隐藏的不确定性.对于一门编程语言同样如此,我将以 ...
- 大话JS面向对象之扩展篇 面向对象与面向过程之间的博弈论(OO Vs 过程)------(一个简单的实例引发的沉思)
一,总体概要 1,笔者浅谈 我是从学习Java编程开始接触OOP(面向对象编程),刚开始使用Java编写程序的时候感觉很别扭(面向对象式编程因为引入了类.对象.实例等概念,非常贴合人类对于世间万物的认 ...
- 原生Ajax用法——一个简单的实例
Ajax全名(Asynchronous(异步) JavaScript and XML )是可以实现局部刷新的 在讲AJax之前我们先用简单的实例说一下同步和异步这个概念 /*异步的概念(就是当领导有一 ...
- 【转】JMeter基础之——一个简单的性能测试
上一节中,我们了解了jmeter的一此主要元件,那么这些元件如何使用到性能测试中呢.这一节创建一个简单的测试计划来使用这些元件.该计划对应的测试需求. 1)测试目标网站是fnng.cnblogs.co ...
- Django学习 之 Django安装与一个简单的实例认识
一.Django简介 1.MVC与MTV模型 (1)MVC模型 Web服务器开发领域里著名的MVC模式,所谓MVC就是把Web应用分为模型(M),控制器(C)和视图(V)三层,他们之间以一种插件式的. ...
- 从urllib和urllib2基础到一个简单抓取网页图片的小爬虫
urllib最常用的两大功能(个人理解urllib用于辅助urllib2) 1.urllib.urlopen() 2. urllib.urlencode() #适当的编码,可用于后面的post提交 ...
- 【基础】一个简单的MVC实例及故障排除
Controller: public ActionResult Index() { string setting = "ApplicationServices"; var conn ...
- python --爬虫基础 --爬取今日头条 使用 requests 库的基本操作, Ajax
'''思路一: 由于是Ajax的网页,需要先往下划几下看看XHR的内容变化二:分析js中的代码内容三:获取一页中的内容四:获取图片五:保存在本地 使用的库1. requests 网页获取库 2.fro ...
随机推荐
- www.qtbig.com:QList的at与[]10亿次运行速度比较(运行速度at都优于[],但区别不大)
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明.本文链接:https://blog.csdn.net/nicai_xiaoqinxi/artic ...
- 聊一聊,React开发中应该规避的点
原文永久链接: https://github.com/AttemptWeb..... 下面说到的React开发中注意的问题,部分是自己遇到过的点,部分是收集的,也算是React代码优化部分,这次做一个 ...
- FireWolf OS X PE
FireWolf OS X PE FireWolf OS X PE 9 使用手册 https://pe.firewolf.app/manual/ https://pe.firewolf.app/m ...
- vue 生命周期的详解
一.vue生命周期的解析 > 1>什么是vue生命周期 每个vue实例在被创建之前都要经过一系列的初始化过程,这个过程就是vue的生命周期.详细来说,就是Vue实例从开始创建,初始化数据, ...
- Js保存图片到本地
注:此方法是使用hbuilderx云打包之后才能用,否则在浏览器中会报 plus is not defined 官方文档 http://www.html5plus.org/doc/zh_cn/gall ...
- android RecyclerView的Linear布局案例
1.先创建 activity_recycle_view.xml 和 activity_recycler_linear_item.xml 如下: <?xml version="1.0&q ...
- Discuz!数据库操作DB类和C::t类介绍
类定义文件 DB类: 文件\source\class\class_core.php class DB extends discuz_database {} discuz_database类定义 文件\ ...
- 【Bug】MQ消息与事务提交
项目联调期间,遇到个bug,涉及MQ消息传递和事务提交时间问题,简单记录下. 背景 审核服务(审核创建项目),点击审核通过,后台代码会在提交事务前发送MQ消息(该消息由项目管理服务消费),发送成功后, ...
- Android笔记(四十) Android中的数据存储——SQLite(二) insert
准备工作: 我们模拟一个注册的页面,先看UI 我们需要创建一个数据库:user,数据库包含表user,user表包含字段id.username.password.mobilephone MainAct ...
- Python基础Day1—上
一.计算机基础 CPU:中央处理器,相当于人的大脑:运算中心与控制中心的结合. 内存:临时存储数据,与CPU交互. 硬盘:永久存储数据. 内存的优点:读取速度快 内存的缺点:容量小,造价高,断电数据会 ...