Python网络数据采集- 创建爬虫
1. 初见网络爬虫
1.1 网络连接
输出某个网页的全部 HTML 代码。
urllib 是 Python 的标准库(就是说你不用额外安装就可以运行这个例子),包含了从网络请求数据,处理 cookie,甚至改变像请求头和用户代理这些元数据的函数。
from urllib.request import urlopen html = urlopen("http://cn.bing.com") print(html.read())
1.2 BeautifulSoup
BeautifulSoup 尝试化平淡为神奇。它通过定位 HTML 标签来格式化和组织复杂的网络信息,用简单易用的 Python 对象为我们展现 XML 结构信息。
from urllib.request import urlopen from bs4 import BeautifulSoup html = urlopen("http://cn.bing.com") bsObj = BeautifulSoup(html.read()) print(bsObj.h1)
下面的所有函数调用都可以产生同样的结果。
urlopen代码主要可能会发生两种异常:
- 网页在服务器上不存在(或者获取页面的时候出现错误)
- 服务器不存在
第一种异常发生时,程序会返回“HTTPError”异常。
如果服务器不存在(就是说链接打不开,或者是 URL 链接写错了),urlopen 会返回一个 None 对象。
如果调用 None 对象下面的子标签,就会发生 AttributeError 错误。
2. 复杂HTML解析
2.1 BeautifulSoup进阶
1. 标签
基本上每个网站都会有层叠样式表(Cascading Style Sheet,CSS)。CSS 的发明是网络爬虫的福音。CSS 可以让 HTML 元素呈现出差异化,
使那些具有完全相同修饰的元素呈现出不同的样式。
网络爬虫可以通过 class 属性的值,轻松地区分出两种不同的标签。
获取博客园首页上的文章标题。
html源码:
from urllib.request import urlopen from bs4 import BeautifulSoup html = urlopen("https://www.cnblogs.com/") bsObj = BeautifulSoup(html) titleList = bsObj.findAll("a", {"class": "titlelnk"}) for title in titleList: print(title.get_text())
注:.get_text() 会把正在处理的 HTML 文档中所有的标签都清除,然后返回一个只包含文字的字符串。
find和findAll。
findAll(tag, attributes, recursive, text, limit, keywords)
find(tag, attributes, recursive, text, keywords)</DI< div>
标签参数 tag 可以接受一个标签的名称或多个标签名称组成的 Python 列表。
例如,下面的代码将返回一个包含 HTML 文档中所有标题标签的列表。
.findAll({"h1","h2","h3","h4","h5","h6"})
属性参数 attributes 是用一个 Python 字典封装一个标签的若干属性和对应的属性值。例如,下面这个函数会返回 HTML 文档里红色与绿色两种颜色的 span 标签。
.findAll("span", {"class":{"green", "red"}})
递归参数 recursive 是一个布尔变量。如果 recursive 设置为 True,findAll 就会根据要求去查找标签参数的所有子标签,以及子标签的子标签。如果 recursive 设置为 False,findAll 就只查找文档的一级标签。
文本参数 text 有点不同,它是用标签的文本内容去匹配,而不是用标签的属性。
List = bsObj.findAll(text = "园子") print(len(List))
结果为1。
find 其实等价于 findAll 的 limit 等于1 时的情形。
还有一个关键词参数 keyword,可以用于选择那些具有指定属性的标签。
List = bsObj.findAll(id = "site_nav_top") print(List)
[<div id="site_nav_top">代码改变世界</div>]
下面两行代码是完全一样的:
class 是 Python 语言的保留字,在 Python 程序里是不能当作变量或参数名使用的,可以在 class 后面增加一个下划线或者把 class 用引号包起来。
2. 导航树
在 BeautifulSoup 库里,孩子(child)和后代(descendant)有显著的不同:和人类的家谱一样,子标签就是一个父标签的下一级,而后代标签是指一个父标签下面所有级别的标签。
一般情况下,BeautifulSoup 函数总是处理当前标签的后代标签。例如,bsObj.body.h1 选择了 body 标签后代里的第一个 h1 标签,不会去找 body 外面的标签。
类似地,bsObj.div.findAll("img") 会找出文档中第一个 div 标签,然后获取这个 div 后代里所有的 img 标签列表。
- 使用 .children 找出子标签;
- 使用 .next_sibling(s) 获取后面的兄弟标签;
- 类似的 .previous_sibling(s) 向前获取兄弟标签;
- 使用 .parent(s) 获取父标签。
2.2 正则表达式
正则表达式可以作为 BeautifulSoup 语句的任意一个参数,让目标元素查找工作极具灵活性。
查看当前首页有多少提到“数据”的随笔。
from urllib.request import urlopen from bs4 import BeautifulSoup import re html = urlopen("https://www.cnblogs.com/") bsObj = BeautifulSoup(html) List = bsObj.findAll(text = re.compile(".*数据.*")) print(len(List))
2.3 获取属性
对于一个标签对象,可以用下面的代码获取它的全部属性:
myTag.attrs
要注意这行代码返回的是一个 Python 字典对象,可以获取和操作这些属性。比如要获取图片的资源位置 src,可以用下面这行代码:
myImgTag.attrs["src"]
在网络数据采集时经常不需要查找标签的内容,而是需要查找标签属性。比如标签指向的 URL 链接包含在 href 属性中,或者标签的图片文件包含在 src 属性中,这时获取标签属性就变得非常有用了。
获取首页大家的头像地址。
from urllib.request import urlopen from bs4 import BeautifulSoup import re html = urlopen("https://www.cnblogs.com/") bsObj = BeautifulSoup(html) text = "https:\/\/pic\.cnblogs\.com\/face.+\.png" imgList = bsObj.findAll("img", {"src": re.compile(text)}) for img in imgList: print(img["src"]) print(len(imgList))
2.4 Lambda表达式
Lambda 表达式本质上就是一个函数,可以作为其他函数的变量使用。
BeautifulSoup 允许我们把特定函数类型当作 findAll 函数的参数。唯一的限制条件是这些函数必须把一个标签作为参数且返回结果是布尔类型。BeautifulSoup 用这个函数来评估它遇到的每个标签对象,最后把评估结果为“真”的标签保留,把其他标签剔除。
例如:
bsObj.findAll(lambda tag: len(tag.attrs) == 2)
2.5 单个域名采集
目标网址:https://baike.sogou.com/v58828.htm?fromTitle=Python
采集搜狗百科上Python词条简介里指向其他词条的链接。
词条链接有以下特点:
- class属性为"ed_inner_link";
- target属性为"_blank";
- 链接以"/lemma/ShowInnerLink.htm"开头。
为了避免一个页面被采集两次,链接去重是非常重要的。在代码运行时,把已发现的所有链接都放到一起,并保存在集合 set 中。这样,只有“新”链接才会被采集,之后再从页面中搜索其他链接。
通过爬虫在页面之间相互跳转:
from urllib.request import urlopen from bs4 import BeautifulSoup import re import random def getLinks(articleUrl): html = urlopen("https://baike.sogou.com"+articleUrl) bsObj = BeautifulSoup(html) find = "\/lemma\/ShowInnerLink\.htm.+" return bsObj.findAll("a", {"class": "ed_inner_link", "target": "_blank", "href": re.compile(find)}) links = getLinks("/v58828.htm?fromTitle=python") for i in range(0,10): if len(links) > 0: ran = random.randint(0, len(links)-1) newArticle = links[ran].attrs["href"] print(i, links[ran].get_text()) links = getLinks(newArticle) else: print("爬虫中断") break
4. 用Scrapy采集
Scrapy 就是一个大幅度降低网页链接查找和识别工作复杂度的 Python 库,它可以让用户轻松地采集一个或多个域名的信息。
教程:https://scrapy-chs.readthedocs.io/zh_CN/0.24/intro/tutorial.html
4.1 创建项目
在开始爬取之前,必须创建一个新的Scrapy项目。进入打算存储代码的目录中,运行下列命令:
该命令将会创建包含下列内容的 tutorial 目录:
scrapy.cfg
: 项目的配置文件;tutorial/
: 该项目的python模块。之后将在此加入代码;tutorial/items.py
: 项目中的item文件;tutorial/pipelines.py
: 项目中的pipelines文件;tutorial/settings.py
: 项目的设置文件;tutorial/spiders/
: 放置spider代码的目录。
4.2 定义Item
Item 是保存爬取到的数据的容器;其使用方法和 python 字典类似, 并且提供了额外保护机制来避免拼写错误导致的未定义字段错误。
这里爬取博客园首页的文章信息(20条)。
import scrapy class ArticleInfo(scrapy.Item): title = scrapy.Field()#随笔标题 link = scrapy.Field()#随笔链接 author = scrapy.Field()#随笔作者 read = scrapy.Field()#随笔阅读量
4.3 编辑myspider.py
根据html结构:
<div class="post_item_body"> <h3><a class="titlelnk" href="https://www.cnblogs.com/lxfweb/p/12831417.html" target="_blank">DVWA-对Command Injection(命令注入)的简单演示与分析</a></h3> <p class="post_item_summary"> <a href="https://www.cnblogs.com/lxfweb/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/1996712/20200405145811.png" alt=""/></a> 前言 上一篇文章中,对命令注入进行了简单的分析,有兴趣的可以去看一看,文章地址 https://www.cnblogs.com/lxfweb/p/12828754.html,今天这篇文章以DVWA的Command Injection(命令注入)模块为例进行演示与分析,本地搭建DVWA程序可以看这篇文 ... </p> <div class="post_item_foot"> <a href="https://www.cnblogs.com/lxfweb/" class="lightblue">雪痕*</a> 发布于 2020-05-05 17:10 <span class="article_comment"><a href="https://www.cnblogs.com/lxfweb/p/12831417.html#commentform" title="0001-01-01 08:05" class="gray"> 评论(0)</a></span><span class="article_view"><a href="https://www.cnblogs.com/lxfweb/p/12831417.html" class="gray">阅读(9)</a></span></div> </div>
在spiders文件夹下创建并编写myspider.py文件。
import scrapy from tutorial.items import ArticleInfo class MySpider(scrapy.Spider): # 设置name name = "cnblogs" # 设定域名 allowed_domains = ["www.cnblogs.com"] # 填写爬取地址 start_urls = ["https://www.cnblogs.com/"] # 编写爬取方法 def parse(self, response): for line in response.xpath('//div[@class="post_item_body"]'): # 初始化item对象保存爬取的信息 item = ArticleInfo() # 这部分是爬取部分,使用xpath的方式选择信息,具体方法根据网页结构而定 item['title'] = line.xpath('.//a[@class="titlelnk"]/text()').extract() item['link'] = line.xpath('.//a[@class="titlelnk"]/@href').extract() item['author'] = line.xpath('.//div[@class="post_item_foot"]/a/text()').extract() item['read'] = line.xpath('.//span[@class="article_view"]/a/text()').extract() yield item
- name:scrapy唯一定位实例的属性,必须唯一
- allowed_domains:允许爬取的域名列表,不设置表示允许爬取所有
- start_urls:起始爬取列表
- parse:回调函数,处理请求并返回处理后的数据和需要跟进的url
4.4 开始爬取
进入tutorial文件夹,执行命令
scrapy crawl cnblogs -o items.json
可以看到部分结果如下:
4.5 观察数据
import json with open('tutorial/items.json', 'r', encoding='utf-8') as f: rownum = 0 new_list = json.load(f) for i in new_list: rownum += 1 print("line{}: title:{}\nlink:{}\nauthor:{}\nread:{}\n".format(rownum, i['title'][0], i['link'][0], i['author'][0], i['read'][0]))
可以看到输出为:
5. XPath
https://www.runoob.com/xpath/xpath-tutorial.html
- XPath 使用路径表达式在 XML 文档中进行导航;
- XPath 包含一个标准函数库;
- XPath 是 XSLT 中的主要元素;
- XPath 是一个 W3C 标准。
5.1 XPath节点
在 XPath 中,有七种类型的节点:元素、属性、文本、命名空间、处理指令、注释以及文档(根)节点。XML 文档是被作为节点树来对待的。树的根被称为文档节点或者根节点。
<?xml version="1.0" encoding="UTF-8"?> <bookstore> <book> <title lang="en">Harry Potter</title> <author>J K. Rowling</author> <year>2005</year> <price>29.99</price> </book> </bookstore>
节点:
<bookstore> (文档节点) <author>J K. Rowling</author> (元素节点) lang="en" (属性节点)
节点关系:
- 父(Parent): 每个元素以及属性都有一个父。在下面的例子中,book 元素是 title、author、year 以及 price 元素的父;
- 子(Children): 元素节点可有零个、一个或多个子。在下面的例子中,title、author、year 以及 price 元素都是 book 元素的子;
- 同胞(Sibling): 拥有相同的父的节点。在下面的例子中,title、author、year 以及 price 元素都是同胞;
- 先辈(Ancestor): 某节点的父、父的父,等等。在下面的例子中,title 元素的先辈是 book 元素和 bookstore 元素:
- 后代(Descendant): 某个节点的子,子的子,等等。在下面的例子中,bookstore 的后代是 book、title、author、year 以及 price 元素。
5.2 XPath语法
XPath 使用路径表达式来选取 XML 文档中的节点或节点集。节点是通过沿着路径 (path) 或者步 (steps) 来选取的。
<?xml version="1.0" encoding="UTF-8"?> <bookstore> <book> <title lang="eng">Harry Potter</title> <price>29.99</price> </book> <book> <title lang="eng">Learning XML</title> <price>39.95</price> </book> </bookstore>
1. 选取节点
2. 谓语
谓语用来查找某个特定的节点或者包含某个指定的值的节点。
谓语被嵌在方括号中。
3. 未知节点
XPath 通配符可用来选取未知的 XML 元素。
4. 若干路径
通过在路径表达式中使用"|"运算符,您可以选取若干个路径。
5.3 XPath轴(Axes)
轴可定义相对于当前节点的节点集。
5.4 XPath运算符
XPath 表达式可返回节点集、字符串、逻辑值以及数字。
Python网络数据采集- 创建爬虫的更多相关文章
- 学习爬虫:《Python网络数据采集》中英文PDF+代码
适合爬虫入门的书籍<Python网络数据采集>,采用简洁强大的Python语言,介绍了网络数据采集,并为采集新式网络中的各种数据类型提供了全面的指导.第一部分重点介绍网络数据采集的基本原理 ...
- 笔记之Python网络数据采集
笔记之Python网络数据采集 非原创即采集 一念清净, 烈焰成池, 一念觉醒, 方登彼岸 网络数据采集, 无非就是写一个自动化程序向网络服务器请求数据, 再对数据进行解析, 提取需要的信息 通常, ...
- Python网络数据采集PDF
Python网络数据采集(高清版)PDF 百度网盘 链接:https://pan.baidu.com/s/16c4GjoAL_uKzdGPjG47S4Q 提取码:febb 复制这段内容后打开百度网盘手 ...
- [python] 网络数据采集 操作清单 BeautifulSoup、Selenium、Tesseract、CSV等
Python网络数据采集操作清单 BeautifulSoup.Selenium.Tesseract.CSV等 Python网络数据采集操作清单 BeautifulSoup.Selenium.Tesse ...
- Python网络数据采集PDF高清完整版免费下载|百度云盘
百度云盘:Python网络数据采集PDF高清完整版免费下载 提取码:1vc5 内容简介 本书采用简洁强大的Python语言,介绍了网络数据采集,并为采集新式网络中的各种数据类型提供了全面的指导.第 ...
- Python网络数据采集7-单元测试与Selenium自动化测试
Python网络数据采集7-单元测试与Selenium自动化测试 单元测试 Python中使用内置库unittest可完成单元测试.只要继承unittest.TestCase类,就可以实现下面的功能. ...
- Python网络数据采集6-隐含输入字段
Python网络数据采集6-隐含输入字段 selenium的get_cookies可以轻松获取所有cookie. from pprint import pprint from selenium imp ...
- Python网络数据采集4-POST提交与Cookie的处理
Python网络数据采集4-POST提交与Cookie的处理 POST提交 之前访问页面都是用的get提交方式,有些网页需要登录才能访问,此时需要提交参数.虽然在一些网页,get方式也能提交参.比如h ...
- Python网络数据采集3-数据存到CSV以及MySql
Python网络数据采集3-数据存到CSV以及MySql 先热热身,下载某个页面的所有图片. import requests from bs4 import BeautifulSoup headers ...
随机推荐
- Scala函数式编程(六) 懒加载与Stream
前情提要 Scala函数式编程指南(一) 函数式思想介绍 scala函数式编程(二) scala基础语法介绍 Scala函数式编程(三) scala集合和函数 Scala函数式编程(四)函数式的数据结 ...
- Centos7 安装redis 5.0.8 开机自启动
redis安装 将安装包移动到linux上 执行解压 tar -xvf redis-5.0.8.tar.gz 修改redis文件夹名称 mv redis- redis 修改redis权限 chmod ...
- springboot web项目创建及自动配置分析(thymeleaf+flyway)
@ 目录 源码分析 webjars thymeleaf thymeleaf语法 springmvc 启动配置原理 集成flyway插件 springboot 创建web项目只需要引入对应的web-st ...
- 六、路由详细介绍之动态路由RIP(了解一下就行)
动态路由分为距离矢量路由(RIP)和链路状态(OSPF和ISIS) 一.离矢量路由协议-RIP RIP协议现在基本上被淘汰. RIP动态路由协议工作原理,如上图: R12中有192.168.1.0和1 ...
- k8s Service学习
service的概念 kubernetes service定义了一个抽象概念,一个pod的逻辑分组,一种可以访问的策略---通常称为服务.这组pod能够被service访问到,通常通过label se ...
- 计时线程Runnable和Handler的结合
利用Runnable和Handler,来创建计时线程 private double recodeTime = 0;// 用于计时 private double econdTime = 0;// 用于计 ...
- 小波变换在matlab中的使用
对信号进行一层分解 clc; clear; % 获取噪声信号 load('matlab.mat'); sig = M(1,1:1400); SignalLength = length(sig); %使 ...
- R - C Looooops POJ - 2115 (exgcd)
题目大意:很好理解,一个for循环语句,从a开始到b结束,步长是c,模数是pow(2,k) 问,最少循环多少次,才能到达b,如果永远都到不了b,输出FOREVER 题解:其实就是求一个线性方程,cx= ...
- 装机摸鱼日记01--DDR3AMD专用内存+QHQF(6400T)试水
前些日子在某鱼入手了两条AMD专用内存,宏想的DDR3-1600MHz-8G内存和一块高贵阿苏斯Z170-P-D3主板,然后某宝600多买了一颗QHQF(当然也可以玩QHQJ,更便宜,估计三百多),准 ...
- 2016NOIP普及组T2回文日期
回文日期 分类:枚举,函数 [题目描述] 日常生活中,通过年.月.日这三个要素可以表示出一个唯一确定的日期. 牛牛习惯用8位数字表示一个日期,其中,前4位代表年份,接下来2位代表月份,最后2位代表日期 ...