什么是爬虫
一个用来完成网站请求和提取网站中的数据的自动化的程序。
一个
可以
完成网站请求
提取数据
的自动化
的程序
怎么爬
1 发起请求:通过HTTP库向目标站点发起请求,发送request。
2 获取响应内容:得到一个包含诸多内容的response。
3 解析响应内容:对得到的response进行解析。
4 保存爬取内容
那我爬什么呢
可见即可爬
爬点小电影?
爬点小图片?
爬点嘿嘿嘿小说?
……
解析方式
爬取下来的网页数据该如何处理?
正则
xpath
json
BeautifulSoup
PyQuery
……
别说了 爬就完事了
python如何访问互联网?
需要用到urllib(这是一个包(package),包含了网页地址(URL)和Library(lib),包含了四个模块:request、error、parse、robotparser)
URL = 协议(protocol:http、https、ftp……) + 域名(hostname) +路径(path)
GET请求
import urllib.request #调用urllib包中的request模块
baidu = urllib.request.urlopen('https://www.baidu.com') #获取百度
html = baidu.read().decode('utf-8') #read 读取百度 decode编码
print(html) #打印
--> '太长啦,我就只复制一部分结果'
<!DOCTYPE html>
<!--STATUS OK-->
POST请求
import urlli.parse
import urllib.request
temp = bytes(urllib.parse.urlencode({'word' : 'hello'}),encoding = 'utf-8')
res = urllib.request.urlopen('http://httpbin.org/post' , data=data)
print(res)
-->
b'{\n "args": {}, \n "data": "", \n "files": {}, \n "form": {\n "word": "hello"\n }, \n "headers": {\n "Accept-Encoding": "identity", \n "Content-Length": "10", \n "Content-Type": "application/x-www-form-urlencoded", \n "Host": "httpbin.org", \n "User-Agent": "Python-urllib/3.7"\n }, \n "json": null, \n "origin": "223.12.174.68, 223.12.174.68", \n "url": "https://httpbin.org/post"\n}\n'
Handler ProxyHandler 代理
代理干啥用的?
兄弟别问了好吗,你翻墙不知道偷偷摸摸干点啥的时候用的不就是。。。
import urllib.request
import urllib.parse
#创建handler
proxy_handler = urllib.request.ProxyHandler({'http':'http://211.147.239.101:57281','https':'https://211.147.239.101:57281'})
#创建opener
opener = urllib.request.build_opener(proxy_handler)
#创建headers
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.103 Safari/537.36'}
url = 'http://www.baidu.com'
#Request可以添加请求头等信息
request = urllib.request.Request(url,headers = headers)
res = opener.open(resquest)
print(res.read())
-->
'别问为什么没结果,因为失败了23333,会提示由于目标计算机积极拒绝,无法连接'
cookie
2333
我朋友家有只黑色小泰迪就叫cookie 贼亲~
cookie其实就是入场通行证,一人一证,持证通过
爬虫中主要是用来维持登录状态的一个属性
import urllib.request
import http.cookiejar
#申明cookiejar对象
cookie = http.cookiejar.CookieJar()
#使用handler处理cookie
handler = urllib.request.HTTPCookieProcessor(cookie)
opener = urllib.request.build_opener(handler)
response = opener.open('http://www.baidu.com')
#打印出name和value
for i in cookie:
print(i.name + '=' + i.value)
-->
BAIDUID=CB2FDE6A308D8C02DFC2AA33E27F4D93:FG=1
BIDUPSID=CB2FDE6A308D8C02DFC2AA33E27F4D93
H_PS_PSSID=28884_1431_28777_21087_28768_28723_28838_28585_20718
PSTM=1555998931
delPer=0
BDSVRTM=0
BD_HOME=0
cookie保存成文本文件 前面基本一样 最后需要用cookie.save
import http.cookiejar
import urllib.request
filename = 'cookie.txt'
cookie = http.cookiejar.MozillaCookieJar(filename)
handler = urllib.request.HTTPCookieProcessor(cookie)
opener = urllib.request.build_opener(handler)
response = opener.open('http://www.baidu.com')
cookie.save(ignore_discard=True,ignore_expires=True)
-->
'保存好的文件内容:'
# Netscape HTTP Cookie File
# http://curl.haxx.se/rfc/cookie_spec.html
# This is a generated file! Do not edit.
.baidu.com TRUE / FALSE 3703484264 BAIDUID C27D5E631BA7EF490324B73BA07BA8EC:FG=1
.baidu.com TRUE / FALSE 3703484264 BIDUPSID C27D5E631BA7EF490324B73BA07BA8EC
.baidu.com TRUE / FALSE H_PS_PSSID 1467_28777_21083_28775_28721_28833_28584_26350_28604
.baidu.com TRUE / FALSE 3703484264 PSTM 1556000617
.baidu.com TRUE / FALSE delPer 0
www.baidu.com FALSE / FALSE BDSVRTM 0
www.baidu.com FALSE / FALSE BD_HOME 0
读取cookie
import http.cookiejar
import urllib.request
cookie = http.cookiejarMozillaCookieJar()
#cookie = http.cookiejar.LWPCookieJar() 另一种保存方式
cookie.load('cookie.txt',ignore_discard=True,ignore_expires=True)
handle = urllib.request.HTTPCookieProcessor(cookie)
opener = urllib.request.build_opener(handler)
response = opener.open('http://www.baidu.com')
print(response.read().decode('utf-8'))
-->'复制部分结果'
<html>
<head>
<meta http-equiv="content-type" content="text/html;charset=utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=Edge">
<meta content="always" name="referrer">
<meta name="theme-color" content="#2932e1">
<link rel="shortcut icon" href="/favicon.ico" type="image/x-icon" />
<link rel="search" type="application/opensearchdescription+xml" href="/content-search.xml" title="百度搜索" />
<link rel="icon" sizes="any" mask href="//www.baidu.com/img/baidu_85beaf5496f291521eb75ba38eacbd87.svg">
<link rel="dns-prefetch" href="//s1.bdstatic.com"/>
<link rel="dns-prefetch" href="//t1.baidu.com"/>
<link rel="dns-prefetch" href="//t2.baidu.com"/>
<link rel="dns-prefetch" href="//t3.baidu.com"/>
<link rel="dns-prefetch" href="//t10.baidu.com"/>
<link rel="dns-prefetch" href="//t11.baidu.com"/>
<link rel="dns-prefetch" href="//t12.baidu.com"/>
<link rel="dns-prefetch" href="//b1.bdstatic.com"/>
<title>百度一下,你就知道</title>
URL解析
from urllib.parse import urlparse
urlparse将输入的url进行自动解析分类
from urllib.parse import urlparse
temp = urlparse('https://www.baidu.com/s?ie=UTF-8&wd=baidu')
print(temp)
-->
ParseResult(scheme='https', netloc='www.baidu.com', path='/s', params='', query='ie=UTF-8&wd=baidu', fragment='')
urlunparse将代码完成拼接,逆向完成urlparse过程
from urllib.parse import urlunparse
temp = ['https','www.baidu.com','/s','','ie=UTF-8&wd=baidu','']
print(urlunparse(temp))
-->
https://www.baidu.com/s?ie=UTF-8&wd=baidu
================================================================
来来来 撸一只猫猫
http://placekitten.com
代码撸起来:
import urllib.request
cat = urllib.request.urlopen('http://placekitten.com/200/300')
cat_img = cat.read()
with open('cat_200_300.jpg','wb') as f:
f.write(cat_img)
然后就可以撸到一只猫猫啦比如说这样↓↓↓↓↓↓↓↓
除了cat.read()方法之外,还有其他方法了解一下
cat.geturl() #得到访问地址
-->
'http://placekitten.com/200/300'
cat.info() #得到http的文件对象信息
-->
<http.client.HTTPMessage object at 0x0000000002F00198>
cat.getcode #正常响应
-->
200
cat.status #正常响应
-->
200
cat.getheaders() #请求头
-->
[('Date', 'Mon, 22 Apr 2019 12:37:22 GMT'), ('Content-Type', 'image/jpeg'), ('Transfer-Encoding', 'chunked'), ('Connection', 'close'), ('Set-Cookie', '__cfduid=d4b21eb66a83006fe512f66469a66d6cd1555936642; expires=Tue, 21-Apr-20 12:37:22 GMT; path=/; domain=.placekitten.com; HttpOnly'), ('Access-Control-Allow-Origin', '*'), ('Cache-Control', 'public, max-age=86400'), ('Expires', 'Tue, 23 Apr 2019 12:37:22 GMT'), ('CF-Cache-Status', 'HIT'), ('Vary', 'Accept-Encoding'), ('Server', 'cloudflare'), ('CF-RAY', '4cb7a6106d069756-FRA')]
================================================================
requests库
安装
pip install requests
***requests***库的主要方法
requests.request() #构造请求
requests.get() #获取http中的“主要方法“
requests.head() #获取http中的头部信息 r.header展示头部信息
requests.post() #向http提交post请求,url位置后附加新的数据
requests.put() #向http提交put请求,向url位置存储一个资源覆盖原有资源
requests.patch() #向http提交局部修改,更新局部url位置的资源
requests.delete() #提交删除请求,删除局部url位置资源
***response***对象的属性
r = request.get('http://www.*****.com') #response = requests.get(url) url = http://www.*****.com 请求url中的服务器请求资源
r.status_code #如果返回值=200则正常否则无法正常运行
r.text #url返回的字符串
r.encoding #编码方式
r.apprent_encoding #备选编码方式
r.content #响应内容的二进制形式
r.json #json
r.cookies #cookies
影藏user agent:
r.request.headers #查看header中的user agent
{'User-Agent': 'python-requests/2.21.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive'} #结果
header = {‘user-agaent’:'Mozilla/5.0'} #伪装user-agent
r = request.get(url,headers = header)
{'user-agent': 'Mozilla/5.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive'} #结果
================================================================
百度寻找女神苍井空
import requests
url = 'http://www.baidu.com/s'
kw = {'wd':'苍井空'}
r = requests.get(url, params = kw)
r.status_code
r.request.url
len(r.text)
================================================================
ip地址查询
import requests
ip = str(input('请输入查询的ip地址:')) #应该加一个判断输入数据的类型
url ='http://m.ip138.com/ip.asp?ip=' #但是我懒。。
r = requests.get(url+ip)
print(r.text[-500:])
========================================================================
import requests #讲道理 应该这样写
ip = str(input('请输入查询的ip地址:')) #但是我就是不加判断语句
url ='http://m.ip138.com/ip.asp?ip=' #2333
try:
r = requests.get(url+ip)
r.raise_for_status()
r.ending = r.apparent_encoding
print(r.text[-500:])
except:
print('失败')
================================================================
BeautifulSoup库
解析器:
html:bs4库
lxml:lxml库
xml:lxml库
html5lib:html5lib库
'安装:' pip install lxml/html5lib
'调用:' BeautifulSoup(***,'html.parse'/'lxml'/'xml'/'html5lib') '***为目标代码'
基本元素:
Tag:开头 <>, 结尾</>
Name: 标签的名字 ,
,p就是名字 ;.name
Attribute:.attrs
NavigableString:.string
Comment:注释
BS的遍历方法
下行遍历
.contents:子节点列表,的儿子节点存入列表
.children:子节点的迭代类型,用于循环遍历儿子节点
.descendants:子孙节点的迭代类型,包含所有子孙节点,循环遍历
上行遍历
.parent:节点的父亲标签
.parents:节点先辈标签的迭代类型,用于循环遍历先辈节点
平行遍历
.next_sibling:返回html文本顺序的下一个平行节点标签
.previous_sibling:返回html文本顺序的上一个平行节点标签
.next_siblings:返回html文本顺序的后续所有平行节点标签
.previous_siblings:返回html文本顺序的前续所有平行节点标签
美丽汤的用法
html ='''
<html>
<head> <meta http-equiv="content-type" content="text/html;charset=utf-8"><meta http-equiv="X-UA-Compatible" content="IE=Edge">
<meta content="always" name="referrer">
<meta name="theme-color" content="#2932e1">
<link rel="shortcut icon" href="/favicon.ico" type="image/x-icon" /><link rel="search" type="application/opensearchdescription+xml" href="/content-search.xml" title="百度搜索" />
<link rel="icon" sizes="any" mask href="//www.baidu.com/img/baidu_85beaf5496f291521eb75ba38eacbd87.svg">
<link rel="dns-prefetch" href="//s1.bdstatic.com"/>
<link rel="dns-prefetch" href="//t1.baidu.com"/><link rel="dns-prefetch" href="//t2.baidu.com"/>
<link rel="dns-prefetch" href="//t3.baidu.com"/>
<link rel="dns-prefetch" href="//t10.baidu.com"/>
<link rel="dns-prefetch" href="//t11.baidu.com"/>
<link rel="dns-prefetch" href="//t12.baidu.com"/>
<link rel="dns-prefetch" href="//b1.bdstatic.com"/>
<title>百度一下,你就知道
'''
#并不规范的格式
from bs4 import BeautifulSoup
soup = BeautifulSoup(html,'lxml')
print(soup.prettify()) #整理标签
print(soup.title.string) #打印title中的str
print(soup.meta['content']) #打印meta标签中的content属性
-->
<html>
<head>
<meta content="text/html;charset=utf-8" http-equiv="content-type"/>
<meta content="IE=Edge" http-equiv="X-UA-Compatible"/>
<meta content="always" name="referrer"/>
<meta content="#2932e1" name="theme-color"/>
<link href="/favicon.ico" rel="shortcut icon" type="image/x-icon"/>
<link href="/content-search.xml" rel="search" title="百度搜索" type="application/opensearchdescription+xml"/>
<link href="//www.baidu.com/img/baidu_85beaf5496f291521eb75ba38eacbd87.svg" mask="" rel="icon" sizes="any"/>
<link href="//s1.bdstatic.com" rel="dns-prefetch"/>
<link href="//t1.baidu.com" rel="dns-prefetch"/>
<link href="//t2.baidu.com" rel="dns-prefetch"/>
<link href="//t3.baidu.com" rel="dns-prefetch"/>
<link href="//t10.baidu.com" rel="dns-prefetch"/>
<link href="//t11.baidu.com" rel="dns-prefetch"/>
<link href="//t12.baidu.com" rel="dns-prefetch"/>
<link href="//b1.bdstatic.com" rel="dns-prefetch"/>
<title>
百度一下,你就知道
</title>
</head>
</html> #整理完成
百度一下,你就知道 #title标签的内容
text/html;charset=utf-8 #meta标签下的content属性内容
and
'find_all(name,attrs,recursive,**kwargs)' 返回所有匹配
'find(name,attrs,recursive,**kwargs)' 返回一个匹配
name:标签名 字符串类型
print(soup.find_all('meta'))
-->
[<meta content="text/html;charset=utf-8" http-equiv="content-type"/>, <meta content="IE=Edge" http-equiv="X-UA-Compatible"/>, <meta content="always" name="referrer"/>, <meta content="#2932e1" name="theme-color"/>]
arrts:元素 字典类型
print(soup.find_all('http-equiv':"content-type")) #attrs中存入字典类型的寻找目标的属性
print(soup.find_all(content="IE=Edge"))
-->
[<meta content="text/html;charset=utf-8" http-equiv="content-type"/>] #获取标签
[<meta content="IE=Edge" http-equiv="X-UA-Compatible"/>]
text:标签内容
print(soup.find_all(text='内容')) #内容的查找 返回内容
美丽汤中的CSS选择器
嵌套选择
HTML = '''
<div class="qrcodeCon">
<div id="qrcode">
<div class="qrcode-item qrcode-item-1">
<div class="qrcode-img"></div>
<div class="qrcode-text">
<p><b>百度</b></p>
</div>
</div>
'''
soup = BeautifulSoup(HTML,'lxml')
'class=“巴拉巴拉” 提取class属性的时候只需要 “.巴拉巴拉”就可以'
print(soup.select('.qrcode-text')) #提取class="qrcode-text"下的标签
-->
[<div class="qrcode-text"> #通过class="qrcode-text"属性查找
<p><b>百度</b></p>
</div>]
print(soup.select('div p')) #提取div标签下的p标签 用空格隔开
-->
[<p><b>百度</b></p>] #通过div下的p标签的标签查找
'id="巴拉巴拉" 提取id属性的时候只需要 “#巴拉巴拉”就可以'
print(soup.select('#qrcode .qrcode-img')) # 用#查找id下的以class开头的标签 用空格隔开
-->
[<div class="qrcode-img"></div>]
获取属性 [‘属性名称’]
print(soup.select('div')[0]['class']) #div标签下的class属性
-->
['qrcodeCon']
获取文本 get_text()
print(soup.select('p')[0].get_text()) #打印P标签下的文本
-->
百度
================================================================
主C位:正则表达式
你懂得~
import re #调用re库 燥起来
常用的元字符: . ^ $ * + ? | []
. 通配符 | 可匹配所有除去\n的一个字符(使用re.S可匹配到\n) |
---|
EX = 'py\nthon\nthon123'
'.'通配符 :
r1 = re.findall('.',EX)
print('r1',r1) '没有搜索到\n的结果'
-->r1 ['p', 'y', 't', 'h', 'o', 'n', 't', 'h', 'o', 'n', '1', '2', '3']
r2 = re.findall('.',EX,re.S)
print('r2',r2) '搜索到了\n的结果'
-->r2 ['p', 'y', '\n', 't', 'h', 'o', 'n', '\n', 't', 'h', 'o', 'n', '1', '2', '3']
^ 脱字符 | 从待检查位置开始,用^脱字符后的内容进行匹配检查,若待检查开始位置不匹配则返回空,若待检查开始位置匹配,则返回一个结果(re.M可搜索多个结果);还有一种取反的用法 |
---|
"^"脱字符:
EX = 'woshinibaba\nbaba'
r1 = re.findall('^ba',EX)
print('r1',r1)
-->r1 [] '返回结果为空,因为woshinibaba开头和ba不匹配'
EX1 = 'babashiwo\nbabanihao'
r2 = re.findall('^ba',EX1)
print('r2',r2)
-->r2 ['ba'] '只搜索了第一行的开头,返回了一个结果'
r3 = re.findall('ba',EX1,re.S)
print('r3',r3)
-->r3 ['ba', 'ba', 'ba', 'ba'] '不哔哔了,这样总看的出来吧'
r4 = re.findall('[^ba]',EX)
-->['w', 'o', 's', 'h', 'i', 'n', 'i', '\n'] '取反'
$ | 搜索待匹配项的结尾(re.M可以搜索多行后返回) |
---|
'$':
EX = '2019.3.24'
r1 = re.findall('24$',EX)
print('r1',r1)
-->r1 ['24']
're.M和上面类似,算了还是写一遍吧,我真踏马好'
EX1 = 'wakaka\nkaka'
r2 = re.findall('ka$',EX1,re.M)
print('r2',r2)
-->r2 ['ka', 'ka'] '第一行搜索出一个ka返回,第二行搜索一个ka返回'
* + ? | 依次匹配前一个字符的0或者无限次、1或者无限次、0或者1次 |
---|
'好累啊 不想写栗子了呀'
EX = 'what,whaat,whaaaaat'
r1 = re.findall('a*',EX)
-->r1 ['', '', 'a', '', '', '', '', 'aa', '', '', '', '', 'aaaaa', '', '']
'what,whaat,whaaaaat中遇到a的0~无限次就返回,返回''空的值是说明位置是a的0次'
r2 = re.findall('a+',EX)
-->r2 ['a', 'aa', 'aaaaa'] '只有遇到a的1~n次时候返回,其他时候不返回'
r3 = re.findall('a?',EX) '啥也返回'
-->r3 ['', '', 'a', '', '', '', '', 'a', 'a', '', '', '', '', 'a', 'a', 'a', 'a', 'a', '', '']
往后面看还有最小匹配操作符
{m,n} /{m} | 返回出现m~n次/m次的结果 |
---|
这个很简单不举例子了哈
丨 | 或,与或非里面的或;就是说丨两边的任一表达式得到匹配就会返回匹配项 |
---|
EX = 'woshinibaba'
r1 = re.findall('w|b',EX)
print('r1',r1)
-->r1 ['w', 'b', 'b']
[abc] /[a-z] | a/b/c中的任一字符/a~z任一字符 |
---|
这还举个毛栗子啊
re库的主要功能函数
函数 | 功能 |
---|---|
re.search(pattern,string,flags=0) | 简单说就是在一串字符串(string)中搜索和正则表达式(pattern)一样的第一个位置,返回一个match对象 |
re.match(pattern,string,flags=0) | 从一个字符串的开始位置起匹配正则表达式,返回match对象;如果待匹配的字符串开始位置就与正则表达式不符,则返回空,如果相匹配则返回匹配内容 |
re.findall(pattern,string,flags=0) | 搜素字符串,返回列表类型的所有能匹配到的子串 |
re.split(pattern,string,maxsplit=0,flags=0) | 将一个字符串按照正则表达式匹配结果进行分割,返回列表 |
re.finditer(pattern,string,flags=0) | 搜索字符串,返回一个匹配结果的迭代类型,每个迭代元素是match对象 |
re.sub(pattern,repl,string,count=0,flags=0); repl:替换匹配字符串的字符串,count:匹配的最大替换次数 | 在一个字符串中替换所有匹配正则表达式的子串,返回替换后的字符串 |
pattern:正则表达式;string:待匹配的字符串;flags:正则表达式使用时候的一些控制标记;maxsplit:最大分割数,剩余部分作为最后一个元素 | 关于flags的常用标记:re.I:忽略正则表达式的大小写;re.M:匹配每行的开始部分;re.S:匹配所有字符 |
---|
等价用法;面向对象编译:
'栗子'
'大大的栗子'
pat = re.compile('[0-9]\d{5}')
rst = pat.search('我是栗子111111')
match对象
match对象的属性 | 说明 |
---|---|
.string | 返回待匹配的文本 |
.re | 返回匹配时使用的正则表达式 |
.pos | 返回正则表达式搜索文本的开始位置 |
.endpos | 返回正则表达式搜索文本的结束位置 |
match的方法 | 说明 |
---|---|
.group(0) | 获得匹配后的第一个字符串 |
.start() | 匹配字符串在原始字符串的开始位置 |
.end() | 匹配字符串在原始字符串的结束位置 |
.span() | 返回(.start(),.end()) |
最小匹配操作符
一般都是默认最长匹配。
'举个栗子'
match = re.search(r'WTF.*K','WTFAKBKCKDK') #'WTF.*K' = WTF开头+中间随机+K结尾
match.group(0)
-->'WTFAKBKCKDK' #输出最长的字符串
操作符 | 说明 |
---|---|
*? | 前一个字符0次或者无限次扩展,最小匹配 |
+? | 前一个字符1次或者无限次扩展,最小匹配 |
?? | 前一个字符0次或者一次扩展,最小匹配 |
{m,n}? | 扩展前一个字符m至n次(含n),最小匹配 |
正则爬取豆瓣读书列表
import re
import requests
url = 'https://book.douban.com/'
content = requests.get(url).text
pattern = re.compile('<li.*?cover.*?href="(.*?)".*?title"(.*?)".*?"author">(.*?)</div>.*?<span.*?"year">(.*?)<span>.*?</li>',re.S)
result = re.findall(pattern,content)
for i in result:
url,name,author,date = i
print(url,name,author,date)