py数据爬虫
一,基本原理
爬虫的基本逻辑和人访问网页的逻辑是一样, 即发送请求、获取响应、获取数据、数据清洗。
网址一般由四部分组成,协议,IP地址,资源文件,参数.
我们常爬取的为网页数据,网页数据为html语言,现在来分析一下简单的网页源代码和结构.
<html> 整个网页
<head> 头部
<body> 主体
<div> 某块区域框架
<h1> 不同字号标题
<li> 列表
<img> 图片
<a> 放置链接
<p> 文字内容
而且这种情况下,上述关键词都是成对出现的.
我们随便打开一个网页,右击点击查看网页源代码.都可以看到上面的结构。
可以同过一个小列子感受一个html语言,新建一个txt文档,然后里面写上
<html>
<head>
<title>水果种类</title>
</head>
<body>
<div>
<p1>食物<p1>
<p2>水果种类</p2>
</div>
<div>
<ul>
<li>香蕉</li>
<li>菠萝</li>
<li>榴莲</li>
</ul>
</div>
</body>
</html>
.保存txt文件,然后把文件名后缀换为html,用浏览器打开可以显示为:
基本原理:
- (1)网页请求过程
Request(请求):向服务器发送访问请求。
Response(响应):服务器接到请求后,验证请求有效性,向用户发送响应的内容。 - (2)网页请求的方式
GET:常见的方式,一般用于获取或者查询资源信息,大多数网站使用的方式,响应速度快。
POST:相对于GET,多了以表单形式伤处参数的技能,因此除查询信息外,还可以修改信息。 - (3)HTTP状态值
200:服务器成功返回网页
404:请求网页不存在
403:服务器拒绝请求
二、例子
1.爬取贴吧图片
壁纸吧
通过数据爬虫方式爬取上面链接种的图片,打开网页,右击查看网页源代码,通过观察查找,可以发现图片的源码都是类似于这样的格式
{"post_id":35504257395,"is_anonym":false,"forum_id":318,"thread_id":2460150866,"content":"<br><img pic_type=\"0\" class=\"BDE_Image\" src=\"https:\/\/imgsa.baidu.com\/forum\/w%3D580\/sign=294db374d462853592e0d229a0ee76f2\/e732c895d143ad4b630e8f4683025aafa40f0611.jpg\" pic_ext=\"bmp\" height=\"328\"
其中可以发现图片的url隐藏在“str=” 和"pic_ext"之间,那么我们的逻辑就是通过计算机访问网页,然后在网页种查找在这两个字段之中的url。记录并下载下来。
- 代码:
import urllib.request
import re
url = "https://tieba.baidu.com/p/2460150866?red_tag=2279066162"
# 发送请求获取响应
response=urllib.request.urlopen(url)
# 获取网页源码内容
html=response.read()
# 进行解码
html=html.decode('utf-8')
# 通过正则表达式,表示我们想找的内容。
reg = r'src="(.+?\.jpg)" pic_ext' # 括号里面是正则表达式 .+?表示匹配一次或者多次
imgre=re.compile(reg) #编译正则表达式,返回的是一个regexobject对象,通过调用findall(),match(),search()方法
imglist=re.findall(imgre,html)# 匹配字符串的re.findall(pattern,string)
x = 0
for i in imglist:
urllib.request.urlretrieve(i,'picture/%x.jpg'%x)
x += 1
print(x)
print("done")
然后网页上的图片就自动保存到了和脚本相同路径的picture文件夹中。
2.爬取有道翻译
import requests
import json
def get_translate_data(word=None):
# 获取有道翻译的翻译结果
url = "http://fanyi.youdao.com/translate?smartresult=dict&smartresult=rule"
header = {
'Accept':'application/json, text/javascript, */*; q=0.01',
'Accept-Encoding':'gzip, deflate',
'Accept-Language':'zh-CN,zh;q=0.9',
'Connection':'keep-alive',
'Content-Length':'287',
'Content-Type':'application/x-www-form-urlencoded',
'Cookie':'OUTFOX_SEARCH_USER_ID="-2070186998@10.108.160.18";',
'Host':'fanyi.youdao.com',
'Origin':'http://fanyi.youdao.com',
'Referer':'http://fanyi.youdao.com/',
'User-Agent':'Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.122 Mobile Safari/537.36',
'X-Requested-With':'XMLHttpRequest'
}
payload = {'i':word,
'from':'AUTO',
'to':'AUTO',
'smartresult':'dict',
'client':'fanyideskweb',
'salt':'15863245065463',
'sign':'c4b412184c6f286864877c94db0ca0ac',
'ts':'1586324506546',
'bv':'c2cc50f037b42cb4f36a2ee57addfff2',
'doctype':'json',
'version':'2.1',
'keyfrom':'fanyi.web',
'action':'FY_BY_REALTlME'
}
reponse = requests.post(url,data=payload,headers=header)
content = json.loads(reponse.text)
tgt = content['translateResult'][0][0]['tgt']
return tgt
get_translate_data("我爱数据分析")
Out[1]: 'I love data analysis'
3.爬取淘宝文件包
有时候的数据并不在网页源码中,而是储存在文件包中,一般在搜索框中的联想词汇,都是在数据包中,我们来爬取淘宝搜索框中的联想词。
- 1.打开淘宝,右击检查,换成手机浏览模型
- 2.在搜索框中,查找连衣裙
- 3.在检查模型下,打开Network板块,选择JS,从Name中查找相关包
这样可以先通过我们的“眼力”先确定URL,然后和上面的步骤一样爬取数据。 - 代码
import urllib.request
URL = "https://suggest.taobao.com/sug?q=%E8%BF%9E%E8%A1%A3%E8%A3%99&code=utf-8&area=c2c&nick=&sid=null&callback=jsonp158626325390353444"
RESPONSE = urllib.request.urlopen(URL)
HTML = RESPONSE.read()
HTML=HTML.decode('utf-8')
HTML
通过这样方法就可以爬取数据包中的数据。
4.爬取和风天气数据
- 1,注册和风天气
- 2,登录控制台
- 3,应用管理
- 4,添加应用–>新建应用–>添加key–>key类型为WebAPI
- 代码
import requests
import json
import pandas as pd
key = "xxxxxxxxxxxxxxxxxx" # 即上面应用中的key复制过来即可
group = "cn" # 国内热门城市(热门城市URL参数)
number = "10" # 个数为10个
# 城市URL
cityurl = "https://search.heweather.net/top?group={}&key={}&number={}".format(group,key,number)
# 通过request方式,发送请求并获取响应
cityhtml = requests.get(cityurl)
# 读取json文件,转换为了dict格式.
citydict = json.loads(cityhtml.text)
result = pd.DataFrame() # 定义DateFrame用于保存爬取的结果.
# 通过循环获取城市名称,并设置为参数带入第二个URL
for item in citydict['HeWeather6'][0]['basic']:
city=item['cid']
url = "https://free-api.heweather.net/s6/weather/forecast?location={}&key={}".format(city,key)
strhtml = requests.get(url)
dict = json.loads(strhtml.text)
for cityform in dict["HeWeather6"][0]['daily_forecast']:
loca = dict['HeWeather6'][0]['basic']['location']
print(loca) # 城市名称
date = cityform['date']
print(date) # 日期
cond = cityform['cond_txt_d']
print(cond) # 天气情况
tmp_max = cityform['tmp_max']
print(tmp_max) # 一天最高问
tmp_min = cityform['tmp_min']
print(tmp_min) # 最低温
result = result.append([{"loca":loca,"date":date,"cond":cond,"tmp_max":tmp_max,"tmp_min":tmp_min}])
result.to_csv("HeWeather.csv")
通过查看和风天气中的API文档可以了解到API的参数,其中代码中包含了两个URL,第一个URL为获取中国热门城市,参数group表示范围,取值"cn"表示的是国内的热门城市,第二个参数number表示,显示城市的个数,默认为20,在本代码中取10,即访问的为中国国内的10个热门城市的城市信息.
第二个URL为访问基本的天气数据,参数location 即城市信息,由第一个URL获取. 剩下的代码为保存数据.
具体的相关参数可以和风天气的API文档.
最终可以得到如下的一个天气信息.也可以连接数据库直接保存到数据库当中.
5.BeautifulSoup爬取中国旅游网
BeautifulSuop和urllib都是两种爬取网页数据的包,主要的过程和reuquest爬取一样:
- 1.url
- 2.requsets.get(url)
- 3.beautfulsoup解析(将网页解析成Tag)
- 4.数既清洗
- 5.对解析后的数据进行定位获取
优点:可以根据不同的数据进行针对性的解析,缺点是解析器的选择困难
代码1:BeautifulSoup基本函数
# 如果标签只出现一次,soup.head或者title
html_doc = '''<html>
<head>
<meata charset="utf-8">
<title>武汉旅游景点</title>
<meta name="description" content="武汉旅游景点 精简版"/>
<meta name="author" content="hstking">
</head>
<body>
<div id="content">
<div class="title">
<h3>武汉景点</h3>
</div>
<ul class="table">
<li>景点<a>门票价格</a></li>
</ul>
<ul class = "content">
<li nu="1">东湖<a class="price">60</a></li>
<li nu="2">磨山<a class="price">60</a></li>
<li nu="3">欢乐谷<a class="price">108</a></li>
<li nu="4">海昌极地海洋世界<a class="price">150</a></li>
<li nu="5">玛雅水上乐园<a class="price">150</a></li>
</ul>
</div>
</body>
</html>
'''
# from bs4 import BeautifulSoup
soup = BeautifulSoup(html_doc,"lxml")
soup.head
'''
<head>
<meata charset="utf-8">
<title>武汉旅游景点</title>
<meta content="武汉旅游景点 精简版" name="description"/>
<meta content="hstking" name="author"/>
</meata></head>
'''
# 如果标签只出现一次,soup.head或者soup.title
soup.title
'''
<title>武汉旅游景点</title>
'''
# soup.find(tag,属性)
soup.find('ul')
'''
<ul class="table">
<li>景点<a>门票价格</a></li>
</ul>
'''
soup.find_all('ul')[0]
'''
<ul class="table">
<li>景点<a>门票价格</a></li>
</ul>
'''
soup.find_all('li')[1:5]
'''
[<li nu="1">东湖<a class="price">60</a></li>,
<li nu="2">磨山<a class="price">60</a></li>,
<li nu="3">欢乐谷<a class="price">108</a></li>,
<li nu="4">海昌极地海洋世界<a class="price">150</a></li>]
'''
soup.find('li',attrs={'nu':'3'})
'''
<li nu="3">欢乐谷<a class="price">108</a></li>
'''
soup.find_all('a',attrs={'class':'price'})
'''
[<a class="price">60</a>,
<a class="price">60</a>,
<a class="price">108</a>,
<a class="price">150</a>,
<a class="price">150</a>]
'''
res = soup.find('li',attrs={'nu':1})
res.get_text()
'''
'东湖60'
'''
html_doc2='''
<thml><head><title>The Dormouse' story </title></head>
<body>
<p class="title"><b>The Dormouse' story</b></p>
<p class="story">Once upon a time there were three little sisters and their name were
<a href="http://example.com/elsie" class="sister" id="link1">Elsie</a>,
<a href="http://example.com/lacie" class="sister" id="link2">Lacie</a>and
<a href="http://example.com/tillie" class="sister" id="link3">Tillie</a>
and they lived at the bottom of a well.</p>
<p class="story">...</p>
'''
soup2 = BeautifulSoup(html_doc2,'lxml')
soup2.title
'''
<title>The Dormouse' story </title>
'''
soup2.title.name
'''
'title'
'''
soup2.title.string
'''
"The Dormouse' story "
'''
soup2.title.parent.name
'''
'thml'
'''
soup2.a
''''
<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>
'''
soup2.find_all('a')
'''
[<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>,
<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>,
<a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]
'''
soup2.find(id="link3")
'''
<a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>
'''
for link in soup2.find_all('a'):
print(link.get('href'))
'''
http://example.com/elsie
http://example.com/lacie
http://example.com/tillie
'''
print(soup2.get_text())
'''
The Dormouse' story
The Dormouse' story
Once upon a time there were three little sisters and their name were
Elsie,
Lacieand
Tillie
and they lived at the bottom of a well.
'''
代码2 (爬取中国旅游网)
通过CSS selector爬取数据,selector是一种层级关系的结构,用来更加精确的定位信息。
浏览中国旅游网,爬取首页上的文字,右击文件-检查-copy selector
import requests
from bs4 import BeautifulSoup
url = "http://www.cntour.cn/"
strhtml = requests.get(url)
# print(strhtml.text)
soup = BeautifulSoup(strhtml.text,'lxml')
# 依据CSS seclector路径位置,寻找唯一的特征。css选择器,soup.select()函数,reuurn :list
data = soup.select("#main > div > div.mtop.firstMod.clearfix > div.centerBox > ul.newsList > li.top > a")
data # 通过均为路径查找数据,标签+属性,方法 coy selector
'''
[<a href="http://www.cntour.cn/news/13800/" target="_blank" title="政策加码提振市场 生活消费加速回暖">政策加码提振市场 生活消费加速回暖</a>,
<a href="http://www.cntour.cn/news/13795/" target="_blank" title="国家23部门联合发文促文旅消费">国家23部门联合发文促文旅消费</a>,
<a href="http://www.cntour.cn/news/13791/" target="_blank" title="景区“重启”,准备好了吗">景区“重启”,准备好了吗</a>,
<a href="http://www.cntour.cn/news/13789/" target="_blank" title="美丽中国建设有了评估体系">美丽中国建设有了评估体系</a>]
'''
# 清晰和组织数据
for item in data:
result = {
'title':item.get_text(),
'link':item.get('href'),
'id':re.findall(r'\d+',item.get('href'))
}
print(result)
'''
{'title': '政策加码提振市场 生活消费加速回暖', 'link': 'http://www.cntour.cn/news/13800/', 'id': ['13800']}
{'title': '国家23部门联合发文促文旅消费', 'link': 'http://www.cntour.cn/news/13795/', 'id': ['13795']}
{'title': '景区“重启”,准备好了吗', 'link': 'http://www.cntour.cn/news/13791/', 'id': ['13791']}
{'title': '美丽中国建设有了评估体系', 'link': 'http://www.cntour.cn/news/13789/', 'id': ['13789']}
'''
BeaurifulSoup 本质上就是将html文件源码更加的结构化,这样更加有利于工程师的爬取,但是同时也增加了时间成本。
6.爬取去哪儿网
本例为爬取从出发地到目的地之间的旅游规划信息,在这个例子中共需要3个URL,第一个是主要的信息来源,首先我们假设需要从北京去丽江,然后查找从北京到丽江可以有几种方案。先通过浏览器,找到数据所在的位置
复制其URL;
- 代码
import urllib
import requests
import json
dep="北京"
dep = urllib.request.quote(dep)
query="丽江"
query=urllib.request.quote(query)
pages = 3 # 爬取的页数
for page in range(0,pages):
limit=page*20
url="https://touch.dujia.qunar.com/list?modules=list%2CbookingInfo%2CactivityDetail\
&dep={}&query={}%E8%87%AA%E7%94%B1%E8%A1%8C\
&dappDealTrace=true&mobFunction=%E6%89%A9%E5%B1%95%E8%87%AA%E7%94%B1%E8%A1%8C\
&cfrom=zyx&it=dujia_hy_destination&date=\
&needNoResult=true&originalquery={}%E8%87%AA%E7%94%B1%E8%A1%8C&width=480\
&height=320&quality=90&limit=0,20&includeAD=true&qsact=search".format(dep,query,query,limit)
response=requests.get(url)
response_json=json.loads(response.text)
re = response_json['data']['list']['results']
for item in re:
print(urllib.request.unquote(dep)) #出发地
print(item['coreArrive'][0]) #目的地
print(item['price']) # 价格
print(item['priceDate']) # 时间
print(item['title']) # 信息
- 结果
北京
丽江
1500
2020-04-11
丽江+大理 全程自由用车+直飞丽江+大理(送西双版纳4日游)景点、旅拍、海景、美食、鲜花
北京
丽江
3724
2020-04-12
昆明+大理+丽江 云南昆明+大理+丽江5天自由行&独立专车、铂金酒店、升级洱海海景房、景点自选
。。。
接下来我们使用相同的方法找到网站所有的出发地和所有的国内热门城市,更换上面的出发地和目的地既可以爬取所有的信息。
- 找到出发地的城市信息
然后使用相同的方法去寻找到达地的位置。 - 大代码
import urllib
import requests
import json
import time
import pandas as pd
# 设置DataFrame用于整理结果
pages = 1
result = pd.DataFrame()
# 出发地
url = "https://touch.dujia.qunar.com/depCities.qunar"
strhtml=requests.get(url)
dep_dic = json.loads(strhtml.text)
counts=0
for dep_item in dep_dic['data']:
for dep in dep_dic['data'][dep_item]:
print("出发地为:",dep)
url2="https://diy.dujia.qunar.com/api/destination/list.json?parentId=1309063677&depName={}".format(urllib.request.quote(dep))
time.sleep(0.5)
strhtml=requests.get(url2)
arr_dict=json.loads(strhtml.text)
for arr_item in arr_dict['data']:
for arr in arr_item['child_list']:
arr = arr["dest_name"]
print("出发地:{},目的地:{}".format(dep,arr))
# dep为出发地
# arr为到达地
dep = urllib.request.quote(dep)
arr = urllib.request.quote(arr)
for page in range(0,pages):
limit=page*20
url="https://touch.dujia.qunar.com/list?modules=list%2CbookingInfo%2CactivityDetail\
&dep={}&query={}%E8%87%AA%E7%94%B1%E8%A1%8C\
&dappDealTrace=true&mobFunction=%E6%89%A9%E5%B1%95%E8%87%AA%E7%94%B1%E8%A1%8C\
&cfrom=zyx&it=dujia_hy_destination&date=\
&needNoResult=true&originalquery={}%E8%87%AA%E7%94%B1%E8%A1%8C&width=480\
&height=320&quality=90&limit=0,20&includeAD=true&qsact=search".format(dep,arr,arr,limit)
time.sleep(0.5)
response=requests.get(url)
response_json=json.loads(response.text)
re = response_json['data']['list']['results']
dep = urllib.request.unquote(dep) # 出发地
arr = urllib.request.unquote(arr) # 到达地
for item in re:
counts += 1
result = result.append([{"Departure":dep,"Arrived":arr,"time":item['priceDate'],"price":item['price'],"route":item['title']}])
print("第{}次保存信息完成!".format(counts))
result.to_csv('Qunar.csv')
逻辑非常简单,就是先获取出发地位置,再获取目的位置,再获取两地之间的信息。最后保存信息。注意因为是频繁访问网络,所以加上了time.sleep,防止过频繁而被拒绝。再获取URL和json的信息当中,最可能用到的两个网站,URL和json的解码网站。
URL的解码网站
json的解码网站
最终的部分结果为:
7.selenium 动态爬取去哪网
- 1.下载Chrome浏览器驱动 Chromedriver
下载时注意要和自己的浏览器版本所匹配,查看Chorme版本,在浏览器中输入:chrome://version 。 - 2.定位
在从网页中爬取数据的时候,需要我们把定位到数据,常用的定位分为两种一种时属性定位,一种路径定位。
第一大类 属性定位
id name class tag等
(1) id属性-find_elemnet_by_id()
(2) name 属性-find_element_by_name()
(3) class 属性-find_element_by_class_name()
(4) tag属性-find_element_by_tag_name()
(5) link text-find_element_by_link_text()\
第二大类 路径
1、xpath定位
(1)通过绝对路径定位
driver.findElement(By.xpath(“html/body/div[2]/div/div”))
(2)通过相对路径定位
driver.findElement(By.xpath(//body))
(3)通过属性值定位
driver.findElement(By.xpath(“input[@id=‘su’]”))
driver.findElement(By.xpath(“input[@id=‘su’ and @class=’’]”))
(4)层级定位
driver.findElement(By.xpath("//div/div[2]/from[input[@]id=‘su’]/input"))
2 、CSS selector
(1)id
driver.findElement(By.cssSelector(“input#su”))
(2)calss
driver.findElement(By.cssSelector(“input.su”))
(3)父子元素
案例:爬取去哪网自由行数据
- 代码
from selenium import webdriver
import time
driver=webdriver.Chrome() # 打开浏览器
driver.get("https://fh.dujia.qunar.com/?tf=package") #获取网页
dep = '大理'
query = '丽江'
page = 10
driver.find_element_by_xpath("//*[@id='depCity']").clear() #清空输入框
driver.find_element_by_xpath("//*[@id='depCity']").send_keys(dep) # 输入出发地
driver.find_element_by_xpath("//*[@id='arrCity']").send_keys(query) # 输入目的地
driver.find_element_by_xpath("/html/body/div[2]/div[1]/div[2]/div[3]/div/div[2]/div/a").click() # 点击开始定制
print("dep:%s arr:%s"%(dep,query))
# 找到每个page中的信息
for i in range(page):
time.sleep(13)
routes = driver.find_delements_by_xpath("html/body/div[2]/div[2]/div[6]/div[2]/div")
driver.find_element_by_xpath("//*[hozFilter]/div/div[3]/a[2]").click()
# 接下来的操作就是requests通过自己的需要获取动态的信息......
该过程模拟的就是人操作的过程,1.打开浏览器 2.输入网址 3.在输入出发地和目的地 4.点击开始定制 5.爬取网页信息 6. 清洗数据
Scrapy框架,Scrapy框架是专门针对爬虫设计的一种框架,,现在还没有研究,等研究了以后再写,,
总结
1、正则:url-requests.get(url)-清洗数据
- get
从贴吧爬取数据,
静态爬取,即找到URL,通过正则表达式找到数据源,然后通过request爬取数据,获取数据。 - post
获取有道翻译结果,post相较于get需要上传数据,和请求头。
2、Beautifulsoup
Beautifulsoup-url-requests.get(url)-BeautifulSoup(url,‘lxml’)-清洗数据
爬取中国旅游网,BeautifulSoup即把爬取的数据的经过了格式化,经过格式化后同就可以更方便的找到想找的函数。
3、数据抓包:
数据包的寻找-url-get/post-requsts.get(url)-requests.post(url,data,header)-jison结构化数据-字典索引的方式将数据清洗出来保存
爬取淘宝推荐关键字、爬取去哪网路线
上面两中方法都式从源网页中爬取数据,但是有些数据并不是保存在源网页当中,而是保存在数据包中,这样就需要去寻找数据包,这是一个非常费时间的过程。
4、selenium
selenium动态爬取去哪网
前面的几种方法都是爬取静态数据,selenium可以爬取动态信息,即和人读取数据是一样的,但是这种爬取方式,大部分都花费在了网页的加载上,这也是爬取效率最低的一种方法。