爬虫入门

爬虫入门

1.   概述

本文首先介绍Requests库如何自动爬取HTML页面以及如何自动网络请求提交,随后将会讲解如何阅读网络爬虫排除标准。获取了网页之后用BeautifulSoup库解析HTML页面,然后讲解正则表达式,以及如何用正则表达式提取网页关键信息。当然会有很多实战内容如下:

• 京东商品页面的爬取
• 亚马逊商品页面的爬取
• 百度/360搜索关键字提交
• 网络图片的爬取和存储
• IP地址归属地的自动查询



2.  Requests库介绍

2.1.Requests库主要有7个主要方法:

(1)requests.request() 构造一个请求,支撑以下各方法的基础方法
(2)requests.get() 获取HTML网页的主要方法,对应于HTTP的GET

GET请求获取URL位置的资源


requests.get(url, params=None, **kwargs)

∙ url : 拟获取页面的url链接

∙ params :url中的额外参数,字典或字节流格式,可选

∙ **kwargs:12个控制访问的参数

(3)requests.head() 获取HTML网页头信息的方法,对应于HTTP的HEAD

    HEAD请求获取URL位置资源的响应消息报告,即获得该资源的头部信息

requests.head(url, **kwargs)

(4)requests.post()向HTML网页提交POST请求的方法,对应于HTTP的        POST. POST 请求向URL位置的资源后附加新的数据

requests.post(url, data=None,json=None,**kwargs)

(5)requests.put() 向HTML网页提交PUT请求的方法,对应于HTTP的PUT

  PUT 请求向URL位置存储一个资源,覆盖原URL位置的资源

requests.put(url, data=None,**kwargs)

(6)requests.patch() 向HTML网页提交局部修改请求,对应于HTTP的        PATCH。PATCH 请求局部更新URL位置的资源,即改变该处资源的部分内容

requests.patch(url, data=None,**kwargs)

(7)requests.delete() 向HTML页面提交删除请求,对应于HTTP的DELETE

DELETE 请求删除URL位置存储的资源

requests.delete(url, **kwargs)




2.2.Requests库的13个访问参数

(1)params : 字典或字节序列,作为参数增加到url中

>>> kv = {'key1': 'value1','key2':'value2'}
>>> r = requests.request('GET', 'http://python123.io/ws',params=kv)
>>> print(r.url)
http://python123.io/ws?key1=value1&key2=value2

(2)data : 字典、字节序列或文件对象,作为Request的内容
>>> kv = {'key1' : 'value1' , 'key2' :'value2' }
>>> r = requests.request ('POST' , 'http://python123.io/ws' ,data=kv )
>>> body = '主体内容'
>>> r = requests.request ('POST' , 'http://python123.io/ws' ,data=body )

(3)json : JSON格式的数据,作为Request的内容
>>> kv = {'key1' : 'value1' }
>>> r = requests.request ('POST' , 'http://python123.io/ws' ,json=kv )

(4)headers : 字典,HTTP定制头
>>> hd = {'user‐agent' : 'Chrome/10' }
>>> r = requests.request ('POST' , 'http://python123.io/ws' ,headers=hd )

(5)cookies : 字典或CookieJar,Request中的cookie
(6)auth : 元组,支持HTTP认证功能
(7)files : 字典类型,传输文件

>>> fs = {'file': open('data.xls', 'rb')}
>>> r = requests.request('POST', 'http://python123.io/ws', files=fs)

(8)timeout : 设定超时时间,秒为单位
>>> r = requests.request('GET', 'http://www.baidu.com',timeout=10)

(9)proxies : 字典类型,设定访问代理服务器,可以增加登录认证
>>> pxs = { 'http': 'http://user:pass@10.10.10.1:1234'
'https': 'https://10.10.10.1:4321' }
>>> r = requests.request('GET', 'http://www.baidu.com', proxies=pxs)

(10)allow_redirects : True/False,默认为True,重定向开关
(11)stream : True/False,默认为True,获取内容立即下载开关
(12)verify : True/False,默认为True,认证SSL证书开关
(13)cert : 本地SSL证书路径

2.3.Response对象的属性

(1)r.status_code HTTP请求的返回状态,200表示连接成功404表示失败
(2)r.text HTTP响应内容的字符串形式,即,url对应的页面内容
(3)r.encoding 从HTTP header中猜测的响应内容编码方式
(4)r.apparent_encoding 从内容中分析出的响应内容编码方式
(5)r.content HTTP响应内容的二进制形式

注意:r.encoding:如果header中不存在charset认为编码为ISO‐8859-1

      r.text根据r.encoding显示网页内容

r.apparent_encoding:根据网页内容分析出的编码方式,可以看作是r.encoding的备选

 

2.4.Requests库的异常

r.raise_for_status()在方法内部判断r.status_code是否等于200,不需要增加额外的if语句,该语句便于利用try‐except进行异常处理



2.5.爬取网页的通用代码框架

importrequests
def getHtml(url):
   
try:
        r = requests.get(url
,timeout=30)
        r.raise_for_status()
        r.encoding=r.apparent_encoding
       
return r.text
   
except:
       
return  "error"
url="http://www.baidu.com"
print(getHtml(url))



3.  Robots协议的使用

3.1网络爬虫引发的的问题

(1)Web服务器默认接收人类访问受限于编写水平和目的,网络爬虫将会为Web服务器带来巨大的资源开销
(2)服务器上的数据有产权归属网络爬虫获取数据后牟利将带来法律风险

(3)网络爬虫可能具备突破简单访问控制的能力,获得被保护数据
从而泄露个人隐私

3.2网络爬虫的限制

(1)来源审查:判断User‐Agent进行限制
检查来访HTTP协议头的User‐Agent域,只响应浏览器或友好爬虫的访问

(2)发布公告:Robots协议
告知所有爬虫网站的爬取策略,要求爬虫遵守

3.3.Robots协议

RobotsExclusion Standard,网络爬虫排除标准
(1)作用:网站告知网络爬虫哪些页面可以抓取,哪些不行
(2)形式:在网站根目录下的robots.txt文件

(3)网络爬虫:自动或人工识别robots.txt,再进行内容爬取
(4)约束性:Robots协议是建议但非约束性,网络爬虫可以不遵守,但存在法律风险


4.  Requests库网络爬取实战

实例1:京东商品页面的爬取

(1)商品网址https://item.jd.com/2967929.html


(2)代码实例及结果
import requests
def getHtml(url):
    try:
        r = requests.get(url,timeout=30)
        r.raise_for_status()
        r.encoding=r.apparent_encoding
        return r.text
    except:
        return  "error"
url="https://item.jd.com/2967929.html"
print(getHtml(url))

 

实例2:亚马逊商品页面的爬取

(1)商品网址https://www.amazon.cn/gp/product/B01M8L5Z3Y




这里出现错误主要原因是亚马逊网站识别出了这是爬虫去获取网页信息,这里解决方案是采取伪装成浏览器的方式去访问

(2)实例代码
import requests
url="https://www.amazon.cn/gp/product/B01M8L5Z3Y"
try:
    kv = {'user-agent':'Mozilla/5.0'}
    r=requests.get(url,headers=kv)
    r.raise_for_status()
    r.encoding=r.apparent_encoding
    print(r.text[:1000])
except:
    print("爬取失败")

实例3:百度/360搜索关键词提交

(1)  百度的关键词接口:http://www.baidu.com/s?wd=keyword

(2)  360的关键词接口:http://www.so.com/s?q=keyword

(3)  百度的关键词接口实例代码
import requests
url="http://www.baidu.com/s"
keyword="Java"
try:
    kv = {'wd':keyword}
    r=requests.get(url,params=kv)
    r.raise_for_status()
    r.encoding=r.apparent_encoding
    print(len(r.text))
except:
    print("爬取失败")

(4)  360的关键词接口实例代码
import requests
url="http://www.so.com/s"
keyword="Java"
try:
    kv = {'q':keyword}
    r=requests.get(url,params=kv)
    r.raise_for_status()
    r.encoding=r.apparent_encoding
    print(len(r.text))
except:
    print("爬取失败")

实例4:网络图片的爬取和存储

(1)图片地址:

http://image.nationalgeographic.com.cn/2014/0707/20140707104220398.jpg

(2)图片爬取全代码:
import requests
import os
url="http://image.nationalgeographic.com.cn/2014/0707/20140707104220398.jpg"
root="E://pics//"
path=root+url.split('/')[-1]
try:
    if not os.path.exists(root):
        os.mkdir(root)
    if not os.path.exists(path):
        r=requests.get(url)
        with open(path,'wb') as f:
            f.write(r.content)
            f.close()
            print("文件保存成功")
    else:
        print("文件已存在")
except:
    print("爬虫失败")


实例5:IP地址归属地的自动查询

(1)  网页地址:http://m.ip138.com/ip.asp?ip=ipaddress

(2)  代码实例:
import requests
url="http://m.ip138.com/ip.asp?ip="
try:
    r=requests.get(url+'202.204.80.112')
    r.raise_for_status()
    r.encoding=r.apparent_encoding
    print(r.text[-500:])
except:
    print("爬取失败")

 

5.Beautiful Soup库入门

5 .1 Beautiful Soup库小案例

(1)  页面网址:http://python123.io/ws/demo.html

(2)  实例代码:
import requests
from bs4 import BeautifulSoup
r=requests.get("http://python123.io/ws/demo.html")
demo=r.text
soup=BeautifulSoup(demo,'html.parser')
print(soup.prettify())

 

(3)  解析结果:

5 .2 Beautiful Soup库的理解




5 .3 Beautiful Soup库的引用

(1)Beautiful Soup库,也叫beautifulsoup4 或 bs4
(2)约定引用方式如下,即主要是用BeautifulSoup类
from bs4 import BeautifulSoup
import bs4

5 .4BeautifulSoup类

(1)Tag 标签,最基本的信息组织单元,分别用<>和</>标明开头和结尾

(任何存在于HTML语法中的标签都可以用soup.<tag>访问获得,当HTML文    档中存在多个相同<tag>对应内容时,soup.<tag>返回第一个)

(2)Name 标签的名字,<p>…</p>的名字是'p',格式:<tag>.name
(3)Attributes 标签的属性,字典形式组织,格式:<tag>.attrs
(4)NavigableString 标签内非属性字符串,<>…</>中字符串,格式:<tag>.string
(5)Comment 标签内字符串的注释部分,一种特殊的Comment类型
(6)举例说明:

import requests
from bs4 import BeautifulSoup
import bs4
r=requests.get("http://python123.io/ws/demo.html")
demo=r.text
soup=BeautifulSoup(demo,'html.parser')
print(soup.title)
tag=soup.a
print(tag)
print(tag.name)
print(tag.parent.name)
print(tag.attrs['class'])
print(type(tag))
print(tag.string)

输出结果:

a

p

['py1']

<class'bs4.element.Tag'>

BasicPython

5 .5基于bs4库的HTML内容遍历方法

(1)HTML基本格式


(2)标签树的下行遍历

.contents子节点的列表,将<tag>所有儿子节点存入列表
.children 子节点的迭代类型,与.contents类似,用于循环遍历儿子节点
.descendants 子孙节点的迭代类型,包含所有子孙节点,用于循环遍历
BeautifulSoup类型是标签树的根节点

举例说明:

print(soup.head)
print(soup.head.contents)

<head><title>Thisis a python demo page</title></head>

[<title>Thisis a python demo page</title>]

遍历方法:

forchild in soup.body.children:
print(child)
for child in soup.body.descendants:
print(child)

(3)标签树的上行遍历

.parent节点的父亲标签
.parents 节点先辈标签的迭代类型,用于循环遍历先辈节点

遍历所有先辈节点,包括soup本身,所以要区别判断

使用实例:

for parent in soup.a.parents:
    if parent is None:
        print(parent)
    else:
        print(parent.name)

p

body

html

[document]

(4)标签树的平行遍历、

.next_sibling返回按照HTML文本顺序的下一个平行节点标签

.previous_sibling返回按照HTML文本顺序的上一个平行节点标签

.next_siblings迭代类型,返回按照HTML文本顺序的后续所有平行节点标签

.previous_siblings迭代类型,返回按照HTML文本顺序的前续所有平行节点标签

5.6 基于bs4库的HTML格式输出

(1) bs4库的prettify()方法

.prettify()为HTML文本<>及其内容增加更加'\n'

.prettify()可用于标签,方法:<tag>.prettify()

(2)  bs4库的编码

bs4库将任何HTML输入都变成utf‐8编码

Python3.x默认支持编码是utf‐8,解析无障碍




6.  信息标记与提取方法

6.1 信息的标记

标记后的信息可形成信息组织结构,增加了信息维度

标记的结构与信息一样具有重要价值

标记后的信息可用于通信、存储或展示

标记后的信息更利于程序理解和运用

6.2 HTML的信息标记

 

6.3 XML信息标记


6.4 信息提取的一般方法

方法一:完整解析信息的标记形式,再提取关键信息
需要标记解析器,例如:bs4库的标签树遍历
优点:信息解析准确
缺点:提取过程繁琐,速度慢

方法二:无视标记形式,直接搜索关键信息

对信息的文本查找函数即可

优点:提取过程简洁,速度较快

缺点:提取结果准确性与信息内容相关

方法三:结合形式解析与搜索方法,提取关键信息

实例:提取HTML中所有URL链接

思路:1) 搜索到所有<a>标签

2) 解析<a>标签格式,提取href后的链接内容

r=requests.get("http://python123.io/ws/demo.html")
demo=r.text
soup=BeautifulSoup(demo,'html.parser')
for link  in soup.find_all('a'):
    print(link.get('href'))
 

6.5基于bs4库的HTML内容查找方法

(1)<>.find_all(name,attrs, recursive, string, **kwargs)方法

返回一个列表类型,存储查找的结果

<1>∙ name : 对标签名称的检索字符串

print(soup.find_all('a'))

[<a class="py1" href="http://www.icourse163.org/course/BIT-268001"id="link1">BasicPython</a>, <a class="py2"href="http://www.icourse163.org/course/BIT-1001870001"id="link2">Advanced Python</a>]

for tag in soup.find_all(True):
    print(tag.name)

html

head

title

body

p

b

p

a

a

for tag in soup.find_all(re.compile('b')):
    print(tag.name)

body

b

<2>  attrs:对标签属性值的检索字符串,可标注属性检索

import  re
print(soup.find_all(id=re.compile('link')))

<3> recursive: 是否对子孙全部检索,默认True

<4> string: <>…</>中字符串区域的检索字符串

import  re
print(soup.find_all(string=re.compile('python')))

['This is a python demo page', 'The demo python introducesseveral python courses.']

(2) <tag>(..) 等价于 <tag>.find_all(..)

soup(..) 等价于 soup.find_all(..)

 

(3)扩展方法

<>.find() 搜索且只返回一个结果,同.find_all()参数

<>.find_parents() 在先辈节点中搜索,返回列表类型,同.find_all()参数

<>.find_parent() 在先辈节点中返回一个结果,同.find()参数

<>.find_next_siblings() 在后续平行节点中搜索,返回列表类型,同.find_all()参数

<>.find_next_sibling() 在后续平行节点中返回一个结果,同.find()参数

<>.find_previous_siblings() 在前序平行节点中搜索,返回列表类型,同.find_all()参数

<>.find_previous_sibling() 在前序平行节点中返回一个结果,同.find()参数


7.  正则表达式入门

7.1 正则表达式的概念

正则表达式是用来简洁表达一组字符串的表达式,是一种通用的字符串表达框架,是一种针对字符串表达“简洁” 和“特征” 思想的工具,可以用来判断某字符串的特征归属。

可以用于表达文本类型的特征(病毒、入侵等),同时查找或替换一组字符串,匹配字符串的全部或部分

7.2 正则表达式的常用操作符

. ; 表示任何单个字符

[ ]; 字符集,对单个字符给出取值范围;[abc] 表示a、 b、 c,[a‐z]表示a到z单个字符

[^ ] ;非字符集,对单个字符给出排除范围 [^abc];表示非a或b或c的单个字符

*; 前一个字符0次或无限次扩展 abc* ;表示 ab、 abc、 abcc、 abccc等

+; 前一个字符1次或无限次扩展 abc+ ;表示abc、 abcc、 abccc等

? ;前一个字符0次或1次扩展 abc? ;表示 ab、 abc

| ;左右表达式任意一个 abc|def ;表示 abc、def

{m} ;扩展前一个字符m次 ;ab{2}c表示abbc

{m,n} ;扩展前一个字符m至n次(含n); ab{1,2}c表示abc、 abbc

^ ;匹配字符串开头 ;^abc表示abc且在一个字符串的开头

$ ;匹配字符串结尾 ;abc$表示abc且在一个字符串的结尾

( ); 分组标记,内部只能使用 | 操作符; (abc)表示abc,(abc|def)表示abc、 def

\d ;数字,等价于[0‐9]

\w ;单词字符,等价于[A‐Za‐z0‐9_]

 

例子:

P(Y|YT|YTH|YTHO)?N  'PN'、 'PYN'、 'PYTN'、'PYTHN'、 'PYTHON'

PYTHON+                   'PYTHON'、 'PYTHONN'、 'PYTHONNN' …

PY[TH]ON                    'PYTON'、 'PYHON'

PY[^TH]?ON                'PYON'、 'PYaON'、 'PYbON'、 'PYcON'…

PY{:3}N                        'PN'、 'PYN'、 'PYYN'、 'PYYYN'

^[A‐Za‐z]+$          由26个字母组成的字符串

^[A‐Za‐z0‐9]+$         由26个字母和数字组成的字符串

^‐?\d+$                      整数形式的字符串

^[0‐9]*[1‐9][0‐9]*$    正整数形式的字符串

[1‐9]\d{5}                 中国境内邮政编码,6位

[\u4e00‐\u9fa5]       匹配中文字符

\d{3}‐\d{8}|\d{4}‐\d{7} 国内电话号码,010‐68913536

(([1‐9]?\d|1\d{2}|2[0‐4]\d|25[0‐5]).){3}([1‐9]?\d|1\d{2}|2[0‐4]\d|25[0‐5])  

IP地址字符串形式的正则表达式(IP地址分4段,每段0‐255)

7.3 Python Re库的使用方式

(1)Re库一些基本知识

Re库是Python的标准库,主要用于字符串匹配

re库采用raw string类型表示正则表达式,表示为:r'text'

例如: r'[1‐9]\d{5}'    r'\d{3}‐\d{8}|\d{4}‐\d{7}'

raw string是不包含对转义符再次转义的字符串

re库也可以采用string类型表示正则表达式,但更繁琐

例如:'[1‐9]\\d{5}'     '\\d{3}‐\\d{8}|\\d{4}‐\\d{7}'

建议:当正则表达式包含转义符时,使用raw string

(2)Re库的主要功能函数

re.search() 在一个字符串中搜索匹配正则表达式的第一个位置,返回match对象

re.search(pattern, string, flags=0)

pattern :正则表达式的字符串或原生字符串表示

string :待匹配字符串

flags :正则表达式使用时的控制标记

import re
match =re.search(r'[1-9]\d{5}','BIT 100081')
if match:
    print(match.group(0))

结果:100081

re.match() 从一个字符串的开始位置起匹配正则表达式,返回match对象

import re
match =re.match(r'[1-9]\d{5}','100081 BIT')
if match:
    print(match.group(0))

结果:100081

re.findall() 搜索字符串,以列表类型返回全部能匹配的子串

import re
ls =re.findall(r'[1-9]\d{5}','100081 BIT  100011 BIT')
print(ls)

结果:['100081', '100011']

re.split() 将一个字符串按照正则表达式匹配结果进行分割,返回列表类型

import re
print(re.split(r'[1-9]\d{5}','100081 BIT  100011 BIT'))

结果:['', ' BIT  ', ' BIT']

re.finditer() 搜索字符串,返回一个匹配结果的迭代类型,每个迭代元素是match对

import re
for m in re.finditer(r'[1-9]\d{5}','BIT100081  BIT100081'):
    if m:
        print(m.group(0))

结果:100081

100081

re.sub() 在一个字符串中替换所有匹配正则表达式的子串,返回替换后的字符串

re.sub(pattern, repl, string, count=0, flags=0)

pattern :正则表达式的字符串或原生字符串表示

repl :替换匹配字符串的字符串

string :待匹配字符串

count :匹配的最大替换次数

flags :正则表达式使用时的控制标记

import re
print(re.sub(r'[1-9]\d{5}','zipcode','BIT100081  BIT100081'))

结果:BITzipcode  BITzipcode

 

regex =re.compile(pattern, flags=0)

∙ pattern : 正则表达式的字符串或原生字符串表示

∙ flags : 正则表达式使用时的控制标记

>>> regex =re.compile(r'[1‐9]\d{5}')

然后regex对象有六种方法,与上面函数功能相同

 

将正则表达式的字符串形式编译成正则表达式对

常用标记              说明

re.I re.IGNORECASE  忽略正则表达式的大小写,[A‐Z]能够匹配小写字符

re.M re.MULTILINE  正则表达式中的^操作符能够将给定字符串的每行当作匹配开

                   始

re.S re.DOTALL      正则表达式中的.操作符能够匹配所有字符,默认匹配除换行外           的所有字符

(4)  Re库的match对象

Match对象是一次匹配的结果,包含匹配的很多信息

属性         说明  

.string        待匹配的文本

.re              匹配时使用的patter对象(正则表达式)

.pos           正则表达式搜索文本的开始位置

.endpos     正则表达式搜索文本的结束位置

 

方法         说明

.group(0)   获得匹配后的字符串

.start()       匹配字符串在原始字符串的开始位置

.end()         匹配字符串在原始字符串的结束位置

.span()       返回(.start(),.end())

(5)  Re库的匹配方式

默认采取最大匹配,只要长度输出可能不同的,都可以通过在操作符后增加?变成最小匹配

操作符         说明

*?               前一个字符0次或无限次扩展,最小匹配

+?              前一个字符1次或无限次扩展,最小匹配

??               前一个字符0次或1次扩展,最小匹配

{m,n}?        扩展前一个字符m至n次(含n),最小匹配

  • 7
    点赞
  • 52
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值