赞
踩
爬虫是 模拟用户在浏览器或者App应用上的操作,把操作的过程、实现自动化的程序
当我们在浏览器中输入一个url后回车,后台会发生什么?比如说你输入https://www.baidu.com
简单来说这段过程发生了以下四个步骤:
网络爬虫要做的,简单来说,就是实现浏览器的功能。通过指定url,直接返回给用户所需要的数据, 而不需要一步步人工去操纵浏览器获取。
HTTP协议(HyperText Transfer Protocol,超文本传输协议)目的是为了提供一种发布和接收HTML(HyperText Markup Language)页面的方法。
HTTPS(全称:Hypertext Transfer Protocol over Secure Socket Layer),是以安全为目标的HTTP通道,简单讲是HTTP的安全版。
- (1).通过URL或者文件获取网页,
- (2).分析要爬取的目标内容所在的位置
- (3).用元素选择器(re、BeautifulSoup、Xpath等)快速提取(Raw)目标内容
- (4).处理提取出来的目标内容 (通常整理合成一个 Json)
- (5).存储处理好的目标内容(比如放到 MongoDB、MySQL 之类的数据库,或者写进文件里.)
- import urllib.request as re
- from bs4 import BeautifulSoup
- # 1.使用urllib的request模块,打开一个带有html页面的链接,此时获取的只是一个链接对象
- response = re.urlopen("file:///D:/Project/Python/jyputerTest/Spider/Source/demo01.html")
- # 2.从连接对象中将网页源代码字符串获取到,此时是没有经过格式化的
- html = response.read()
- # 3.通过 BeautifulSoup 对网页源代码字符串进行解析
- soup = BeautifulSoup(html)
- # 4.我们就可以从BeautifulSoup中直接使用标签名作为参数 去直接获取对应 标签 中的数据
- soup.find('tr')
- soup.find_all('tr')
(2).json数据读取
- import urllib.request as re
- # 此时我们解析json使用的模版是json
- import json
-
- # 1.同样此时获取我们文件的连接
- response = re.urlopen("file:///D:/Project/Python/jyputerTest/Spider/Source/demo02.json")
- # 2.将连接中的数据读取为源字符串
- jsonString = response.read()
- # 3.加载数据为json,同时对读取到的json字符串做一个解码
- jsonObject = json.loads(jsonString.decode())
- # 4.对json中的数据进行值的获取(分不同的层级)
- jsonObject['employees'] # 根路径
- jsonObject['employees'][0] # 获取列表中的第一个元素
- jsonObject['employees'][0]['firstName'] # 获取字典中指定的key对应的值(value)
本次实战主要是使用 request库 和 BeautifulSoup 库进行数据的爬取
1),Requests 简介
request库是一个非常好用的请求发送包,它可以自动封装底层python 的网络请求。
其中我们本次课程中是使用的:Requests.get()方法
Requests库的get()方法主要用于获取HTML网页,相当于HTTP的GET。其返回对象response的常用属性如下表所示,我们可通过这些属性获取要访问域名的基本信息。
2),BeautifulSoup 简介
BeautifulSoup库是用Python语言编写一个HTML/XML的解释器。它可以很好地处理不规范的标记并生成剖析树,其本身提供了简单又常用的导航、搜索以及修改剖析树的操作,利用它可以大大缩减编程时间。
简单来说,Beautiful Soup是python的一个库,最主要的功能是从网页抓取数据。
Beautiful Soup提供一些简单的、python式的函数用来处理导航、搜索、修改分析树等功能。它是一个工具箱,通过解析文档为用户提供需要抓取的数据,因为简单,所以不需要多少代码就可以写出一个完整的应用程序。
BeautifulSoup支持Python标准库中的HTML解析器,还支持一些第三方的解析器,如果我们不安装它,则 Python 会使用 Python默认的解析器,lxml 解析器更加强大,速度更快,推荐使用lxml 解析器。
3),网页信息查找与提取
我们使用Chrome浏览器,对于要爬取的网页,可以按F12进入调试模式,也可以选择相应信息右键"检查",亦可进入调试模式,而且直接到达该信息HTML位置,更为方便。
- # 导入requests模块和bs4模块
- import requests
- from bs4 import BeautifulSoup
-
- # 加入请求头,防止网站监测出来我们是爬虫,所以都必须要引入请求;对于有需要 登录 的页面需要加上cookices,那么直接在header字典中将 " 'Cookie': '你的cookie' "添加进去
- header={
- "User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36"
- }
- # 引入要爬取的网页的url
- url = 'http://www.weather.com.cn/weather/101120101.shtml'
-
- # 1、获取网页的响应
- # url,请求头,设置
- response = requests.get(url, headers=header,timeout=30)
- # 2,当网页获取成功的之后,设置网页的编码格式,方式出现中文的乱码
- response.encoding='utf-8'
-
- # 3,从获取到的文本连接中获取对应的网页源代码字符串
- data= response.text
- # print(text)
-
- # 4,通过 BeautifulSoup 对网页源代码字符串进行解析;第二个参数是:指定 BeautifulSoup 使用什么格式进行解析(这里使用的是lxml :在你安装BeautifulSoup的时候默认是没有这个库的,需要你手动去安装 pip install lxml)
- soup = BeautifulSoup(data,'lxml')
- # print(soup)
-
- # find()的作用是只去找 指定样式中 第一个出现的
- ul = soup.find(class_='t clearfix')
- # print(ul)
-
- ul_li = ul.find_all('li')
- # 遍历打印所有的元素
- for item in ul_li:
- # print(item)
- # 根据标签获取值
- # 1.获取当前日期
- day = item.find('h1').text
- # 2.获取当前日期下对应的天气
- wea = item.find(class_='wea').text
- # 3.获取当前日期下对应温度区间
- tem = item.find(class_='tem').text
- # 如果想获取最低气温(可以直接使用下标的方式直接去处理温度区间中的值)
- # print(tem[4:])
- # 4.取风向,使用find函数默认是在item中取第一次出现的em标签;对应的可以直接再去取span标签的值,就直接用“.标签案名”的方式进行获取
- ws = item.find('em').span.attrs['title']
- # 5.把两个风向都取出来的话,可以使用findall,此时先把所有的带有风向的span标签获取出来
- ws_all_span = item.find('em').find_all('span')
- # 使用 推导式 的方式将所有风向获取出来,并返回一个列表
- ws_all = [ws['title'] for ws in ws_all_span]
- # 6.获取风力,先拿到class,再拿到对应的i,对应的文本,然后将 "<" 做一个替换 "小于"
- win = item.find(class_='win').i.text.replace("<","小于")
-
- print('-----------------------------begin---------------------------------------------------------------------')
- print('-----------------------------1:日期-----------------------------------------------------------------')
- print(day)
- print('-----------------------------2:天气-----------------------------------------------------------------')
- print(wea)
- print('-----------------------------3:温度-----------------------------------------------------------------')
- print(tem)
- print('-----------------------------4:只获取第一个风向-----------------------------------------------------------')
- print(ws)
- print('-----------------------------5:两个风向都取出来-----------------------------------------------------')
- # 在输出的时候使用sep指定分隔符
- print(ws_all[0],ws_all[1],sep=',')
- print('-----------------------------6:风级-----------------------------------------------------------------')
- print(win)
- print('-----------------------------end---------------------------------------------------------------------')

每一次执行find() 之后的返回值,仍然是一个对象(bs4.element.Tag),还可以继续调用对象中的 "." 方法。
参数 | 对应含义 |
---|---|
find("XXX") | 传入默认参数表示:查询指定 "XXX" 标签第一次出现的位置对应的属性值 |
find_all("XXX") | 传入默认参数表示:查询指定 "XXX" 标签所有出现的位置对应的属性值 |
find(class_='XXX') | 传入 "class_ " 参数表示:查询指定CSS属性为 "XXX" 的值 |
find(id="XXX") | c传入"id" 参数表示:查找指定id属性为 "XXX" 的值 |
find("XXX").span | 配合 "." 参数的时候,来获取 "XXX" 标签下嵌套的 "span" 标签(在之后的代码中可以对Span标签中的值进行进一步的值的获取) |
find("XXX").span.attrs['title'] | 配合 "attrs["xxx"]" 参数的使用,来获取嵌套在"XXX" 标签下对应 "span "标签中指定的属性值,例如可以获取 "span" 标签中的 "title" 属性的值;同理:如果此时的 "span" 标签 换成 "a" 标签,那么此时再使用 ".attrs["href"]" 就可以获取 "a" 标签中的 "href" 属性的值,从而进行连接的跳转。 |
find("XXX").span['title'] | 和上一个表格中的实现效果是等价的 |
在CSS中,标签名不加任何修饰,类名前加点,id名前加 #,在这里我们也可以利用类似的方法来筛选元素,用到的方法是 soup.select(),返回类型是 list。
以下的 select 方法返回的结果都是列表形式,可以遍历形式输出,然后用 get_text() 方法来获取它的内容;获取将其中的值取出来,此时其类型仍然是一个对象(bs4.element.Tag)
参数 | 对应含义 |
---|---|
soup.select('xxx') | 通过标签名进行查找 |
soup.select('.xxx') | 通过类名进行查找,也就是class属性 |
soup.select('#xxx') | 根据id名查找 |
soup.select('p #xxx') | 组合查找即和写 class 文件时,标签名与类名、id名进行的组合原理是一样的。例如查找 p 标签中,id 等于 xxx 的内容,二者需要用空格分开 |
soup.select("head > title") | 直接子标签查询,也是查询指定标签下的某一个标签。例如查询 p 标签下的 title 标签 |
soup.select('a[class="sister"]') | 根据属性查找:查找时还可以加入属性元素,属性需要用中括号括起来,注意属性和标签属于同一节点,所以中间不能加空格,否则会无法匹配到。 |
此时以 "2.爬虫实战:7日天气预报 爬取" 的结果为例进行数据的保存:
此时已经有了从网页中获取的html文本
- # 导入requests模块和bs4模块
- import requests
- from bs4 import BeautifulSoup
-
- # 加入请求头,防止网站监测出来我们是爬虫,所以都必须要引入请求;对于有需要 登录 的页面需要加上cookices,那么直接在header字典中将 " 'Cookie': '你的cookie' "添加进去
- header={
- "User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36"
- }
- # 引入要爬取的网页的url
- url = 'http://www.weather.com.cn/weather/101120101.shtml'
-
- # 1、获取网页的响应
- # url,请求头,设置访问超时
- response = requests.get(url, headers=header,timeout=30)
- # 2,当网页获取成功的之后,设置网页的编码格式,方式出现中文的乱码
- response.encoding='utf-8'
-
- # 3,从获取到的文本连接中获取对应的网页源代码字符串
- data= response.text
- print(data)

图片文件的保存
- # 使用正则进行匹配
- import re
- # 此时的正则表达式的含义是:以 src=开头,然后 .png 结尾,中间是啥都行,而且此时是懒惰匹配
- images = re.findall(r'src="(.*?\.png)"',data)
-
- # 1.单张图片的保存
- response = requests.get(images[0])
- img = response.content
- title=0
- path = r'./picture/%s.jpg'%(title)
- with open(path,'wb') as f:
- f.write(img)
-
- # 2.批量url连接图片的保存
- # 每个文件的命令方式是从 0 开始递增的
- count = 0
- for url in images:
- response = requests.get(url)
- img = response.content
- count = count + 1
- # 使用C语言的格式(类似与占位符的方式)进行拼接 路径字符串 (此时的路径需要提前创建!!!或者再引入os库将创建文件夹)
- path = r'./picture/%s.jpg'%(count)
- # 以写和二进制的权限,打开文件,此时的文件会自动创建
- with open(path,'wb') as f:
- # 使用写的方式将文件下载到指定的路径汇总
- f.write(img)

1).常用元字符:
代码 | 说明 |
---|---|
. | 匹配除换行符以外的任意字符 |
\w | 匹配字母或数字或下划线或汉字 |
\s | 匹配任意的空白符 |
\d | 匹配数字 |
\b | 匹配单词的开始或结束 |
^ | 匹配字符串的开始 |
$ | 匹配字符串的结束 |
2),字符转义
如果想查找元字符本身的话,得使用\
来取消其特殊含义。比如.
可以使用 \.
的方式进行转义。
3).重复次数:常用限定符
代码/语法 | 说明 |
---|---|
* | 重复零次或更多次 |
+ | 重复一次或更多次 |
? | 重复零次或一次 |
{n} | 重复n次 |
{n,} | 重复n次或更多次 |
{n,m} | 重复n到m次 |
4),字符类
对于需要匹配多个字符的时候,我们需要通过列表的方式进行指定,可以指定多个不同的字符,例如(a,e,i,o,u);或者是指定一个区间,例如:([0-9])
5),反义代码
代码/语法 | 说明 |
---|---|
\W | 匹配任意不是字母,数字,下划线,汉字的字符 |
\S | 匹配任意不是空白符的字符 |
\D | 匹配任意非数字的字符 |
\B | 匹配不是单词开头或结束的位置 |
[^x] | 匹配除了x以外的任意字符 |
[^aeiou] | 匹配除了aeiou这几个字母以外的任意字符 |
6),贪婪与懒惰
当正则表达式中包含能接受重复的限定符时,通常的行为是(在使整个表达式能得到匹配的前提下)匹配尽可能多的字符。以这个表达式为例:a.b,它将会匹配最长的以a开始,以b结束的字符串。如果用它来搜索aabab的话,它会匹配整个字符串aabab。这被称为贪婪*匹配。
有时,我们更需要懒惰匹配,也就是匹配尽可能少的字符。前面给出的限定符都可以被转化为懒惰匹配模式,只要在它后面加上一个问号?。这样.*?就意味着匹配任意数量的重复,但是在能使整个匹配成功的前提下使用最少的重复。现在看看懒惰版的例子吧:
a.*?b匹配最短的,以a开始,以b结束的字符串。如果把它应用于aabab的话,它会匹配aab(第一到第三个字符)和ab(第四到第五个字符)。
代码/语法 | 说明 |
---|---|
*? | 重复任意次,但尽可能少重复 |
+? | 重复1次或更多次,但尽可能少重复 |
?? | 重复0次或1次,但尽可能少重复 |
{n,m}? | 重复n到m次,但尽可能少重复 |
{n,}? | 重复n次以上,但尽可能少重复 |
本次实战主要是使用 request库与 Xpath库进行操作
1),Xpath 简介
Xpath 即为 XML 路径语言(XML Path Language),它是一种用来确定 XML 文档中某部分位置的语言。Xpath 基于 XML 的树状结构,提供在数据结构树中找寻节点的能力。起初 Xpath 的提出的初衷是将其作为一个通用的、介于 Xpointer 与 XSL 间的语法模型。但是Xpath 很快的被开发者采用来当作小型查询语言。
Xpath解析网页的流程:
1.首先通过Requests库获取网页数据;
2.通过网页解析,得到想要的数据或者新的链接;
3.网页解析可以通过 Xpath 解析网页,然后通过定位元素,然后再右键使用Copy的方式获取元素的Xpath信息并获得文本:
file=s.xpath('元素的Xpath信息/text()')
- # 导入requests模块和bs4模块
- import requests
- from lxml import etree
-
- # 引入要爬取的网页的url
- url = 'https://movie.douban.com/subject/1292052/'
- # 加入请求头,防止网站监测出来我们是爬虫,所以都必须要引入请求;对于有需要 登录 的页面需要加上cookices,那么直接在header字典中将 " 'Cookie': '你的cookie' "添加进去
- header={
- "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.113 Safari/537.36"
- }
- # 1、获取网页的响应
- # url,请求头,设置访问超时时间
- response = requests.get(url, headers=header,timeout=30)
- # 2,当网页获取成功的之后,设置网页的编码格式,方式出现中文的乱码
- response.encoding='utf-8'
-
- # 3,从获取到的文本连接中获取对应的网页源代码字符串
- data = response.text
-
- # 4.对获取到的网页源代码字符串使用etree解析
- s=etree.HTML(data)
-
- # 5.通过从网页中Copy的Xpath路径,依次获取电影名称、导演、主演、时长信息
- film=s.xpath('//*[@id="content"]/h1/span[1]/text()')
- director=s.xpath('//*[@id="info"]/span[1]/span[2]/a/text()')
- actor=s.xpath('//*[@id="info"]/span[3]/span[2]/a/text()')
- time=s.xpath('//*[@id="info"]/span[13]/text()')
-
- # 6.显示获取的信息内容
- print('name:',film)
- print('director:',director)
- print('actors:',actor)
- print('duration:',time)

获取电影名称
元素的Xpath信息是需要我们手动获取的,获取方式为:定位目标元素(电影名称:肖申克的救赎),在网站上选取的目标元素上依次点击:右键 > 检查,按住快捷键“shift+ctrl+c”,移动鼠标到对应的元素时即可看到对应网页代码:
在电影标题对应的代码上依次点击 右键 > Copy > Copy XPath,获取电影名称的Xpath:
此时得到的标签示例 "//*[@id="content"]/h1/span[1]
"
- # 1,直接将获取到Xpath打印出来,得到的是一个元素
- print(s.xpath('//*[@id="content"]/h1/span[1]'))
- # 输出结果:[<Element span at 0x1a4e1c0afc8>]
-
- # 2,可以再追加"/text()",获取元素中对应的值
- print(s.xpath('//*[@id="content"]/h1/span[1]/text()'))
- # 输出结果:['肖申克的救赎 The Shawshank Redemption']
同理可以得到其他元素的标签!!!
此时以 "4.爬虫实战:豆瓣电影信息 爬取" 的结果为例进行数据的保存:
此时已经有了 “film”,“director”,“actor”,“time”变量对应的属性值
- # 导入requests模块和bs4模块
- import requests
- from lxml import etree
-
- # 引入要爬取的网页的url
- url = 'https://movie.douban.com/subject/1292052/'
- # 加入请求头,防止网站监测出来我们是爬虫,所以都必须要引入请求;对于有需要 登录 的页面需要加上cookices,那么直接在header字典中将 " 'Cookie': '你的cookie' "添加进去
- header={
- "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.113 Safari/537.36"
- }
- # 1、获取网页的响应
- # url,请求头,请求延迟(时间):主要是在多批次访问的时候,有一个停顿时间,可以防止网站监测为爬虫,也就是伪造一个真实的人的请求
- response = requests.get(url, headers=header,timeout=30)
- # 2,当网页获取成功的之后,设置网页的编码格式,方式出现中文的乱码
- response.encoding='utf-8'
-
- # 3,从获取到的文本连接中获取对应的网页源代码字符串
- data = response.text
-
- # 4.对获取到的网页源代码字符串使用etree解析
- s=etree.HTML(data)
-
- # 5.通过从网页中Copy的Xpath路径,依次获取电影名称、导演、主演、时长信息
- film=s.xpath('//*[@id="content"]/h1/span[1]/text()')
- director=s.xpath('//*[@id="info"]/span[1]/span[2]/a/text()') actor=s.xpath('//*[@id="info"]/span[3]/span[2]/a/text()')
- time=s.xpath('//*[@id="info"]/span[13]/text()')

文件保存代码
- # 对最后文件的输出内容进行重新格式化拼接为一个字符串,然后进行保存
- # 1,首先测试输出效果
- print("1:name: {},\n2:director: {},\n3:actor: {},\n4:time: {}".format(film[0],director[0],actor,time[0]))
- # 2,赋于变量进行保存,然后测试输出
- outData = "1:name: {},\n2:director: {},\n3:actor: {},\n4:time: {}".format(film[0],director[0],actor,time[0])
- print(outData)
- # 3,连接本地文件系统,进行文件夹与文件的创建
- # 4,首先判断要输出的文件是否存在,如果存在则直接进入到指定文件夹中,不存在则创建文件夹
- import os
- if os.path.exists("./output"):
- os.chdir("./output")
- else:
- os.mkdir("./output")
- os.chdir("./output")
- # 5,打开一个文件(若文件不存在则创建一个),然后向文件中写入数据,最后关闭与本地文件系统的连接
- # 此时就是提前创建一个带有指定路径和文件的对象,然后再调用写操作,最后将写操作关闭
- myfile = open('./out.txt','w',encoding='utf-8')
- myfile.write(outData)
- myfile.close()
-
- # 6,如果想追加数据,直接打开文件,然后通过第二个参数指定模式为追加
- # 注意此时是在创建文件对象的同时,通过文件对象带调用写操作,
- with open('./out.txt','a',encoding='utf-8') as file:
- file.write('\n123465')

(2).保存字符串到Mysql数据库中
- # 1,首先在Mysql中建立你要导出的数据库和根据字段的内容创建数
- '''
- create databases test;
- use test;
- create table spiderTest(film varchar(100),director varchar(100),actor varchar(100),time varchar(100)) CHARSET=utf8;
- '''
- # 2,然后获取数据库的连接
- # 3,使用 execute()方法,执行插入语句将内容写入到数据库中
- import pymysql # 导入相应操作Mysql的库文件
-
- conn = pymysql.connect(host='localhost',user='root',password='111111',db='test',port=3306,charset='utf8')
- cursor=conn.cursor() # 连接数据库及光标
-
- # 注意此时对于演员的数据过长,所以只是截取了第一个数据写入的
- cursor.execute("insert into spiderTest(film,director,actor,time) values(%s,%s,%s,%s)",(str(film[0]),str(director[0]),str(actor[0]),str(time[0])))
- # 当执行完插入语句后需要手动提交,才会生效。
- conn.commit()
以上就完成今天所有的步骤了,如果需要的话希望大家可以喜欢
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。