网络爬虫学习笔记

文章目录

Resuests库入门

request 库

request库参考地址

地址2

安装方法:

  1. 管理员权限启动cmd
  2. 输入>> pip install requests

测试:

  1. 启动idle
  2. 输入命令
>>> import requests
>>> r = requests.get("http://www.baidu.com")
>>> r.status_code
200
>>> r.encoding = 'utf-8'
>>> r.text

HTTP协议与Requests库的7个主要方法

HTTP协议

HTTP,Hypertext Transfer Protocol,超文本传输协议。

HTTP是一个基于“请求与相应”模式的、无状态的应用层协议。

HTTP协议采用URL作为定位网络资源的标识。

HTTP协议的URL格式 http://host[:port][path]

host:合法的Internet主机域名或IP地址

port:端口号,缺省端口为80

path:请求资源的路径

HTTP URL实例

http://www.bit.edu.cn

http://220.181.11.188/duty

HTTPURL的理解:

URL是通过HTTP协议存取资源的Internet路径,一个URL对应一个数据资源。

HTTP协议对资源的操作

方法说明
GET请求获取URL位置的资源
HEAD请求获取URL位置资源的响应消息报告,即获得该资源的头部信息
POST请求向URL位置的资源后附加新的数据
PUT请求向URL位置存储一个资源,覆盖原URL位置的资源
PATCH请求局部更新URL位置资源,即改变该处资源的部分内容
DELETE请求删除URL位置存储的资源

理解patch和put的区别

假设URL位置有一组数据UserInfo,包含UserID、UserName等20个字段。

需求:用户修改了UserName,其他不变。

采用patch,仅向URL提交UserName的局部更新请求。

采用PUT,必须将所有20个字段一并提交到url,未提交字段被删除

patch节省网络带宽

Requests库的7个主要方法
方法说明
reuqests.request()构造一个请求支撑以下各方法的基础方法
reuqests.get()获取html页面的主要方法,对应于http的get
reuqests.head()获取http网页头信息的方法,对应于http的Head
reuqests.post()向http页面提交post请求,对应http的head
reuqests.put()向http页面提交put请求,对应http的post
reuqests.patch向html页面提交局部修改请求,对应于http的patch
reuqests.delete()向html页面提交删除请求,对应于http的delete

HTTP协议与Requests库方法

HTTP协议方法Requests库方法功能一致性
GETreuqests.get()一致
HEADreuqests.head()一致
POSTreuqests.post()一致
PUTreuqests.put()一致
PATCHreuqests.patch一致
DELETEreuqests.delete()一致
requests.request(method,url,**kwargs)
  1. method:请求方式,对应get/put/post等7种

r = requests.request(‘GET’,url,**kwargs)

r = requests.request('HEAD,url,**kwargs)

r = requests.request(‘POST’,url,**kwargs)

r = requests.request(‘PUT’,url,**kwargs)

r = requests.request(‘PATCH’,url,**kwargs)

r = requests.request(‘DELETE’,url,**kwargs)

r = requests.request(‘OPTIONS’,url,**kwargs)

  1. url:获取页面的url连接
  2. **kwargs:控制访问参数,共13个,均为可选项
  • params:字典或字节序列,作为参数增加到url中
>>> kv ={'key1':'value1','key2':'value2'}
>>> r = requests.request('GET','http://python123.io/ws',params = kv)
>>> print(r.url)
https://python123.io/ws?key1=value1&key2=value2
  • 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)
  • json:JSON格式的数据,作为Request的内容
>>> kv = {'key1':'value1'}
>>> r = requests.request('POST','http://python123.io/ws',json=kv)
  • headers:字典,HTTP定制头
>>> hd = {'user-agent':'Chrome/10'}
>>> r = requests.request('POST','http://python123.io/ws',headers=hd)
  • cookies:字典或CookieJar,Request中的cookie

  • auth:元组,支持HTTP认证功能

  • files:字典类型,传输文件

>>> fs = {'file':open('C:\\Users\\admin\\Desktop\\data.xlsx','rb')}
>>> r = requests.request('POST','http://python123.io/ws',files=fs)
  • timeout:设定的超时时间,以秒为单位
>>> r = requests.request('GET','http://www.baidu.com',timeout=10)
  • proxies:字典类型,设定访问代理服务器,可以增加登录认证
>>> pxs = {'http':'http://user:pass@10.10.10.1:1234','https':'https://10.10.10.14321'}
>>> r = requests.request('GET','http://www.baidu.com',proxies=pxs)
  • allow_redirects:True/False,默认为True,重定向开关

  • stream:True/False,默认为True,获取内容立即下载开关

  • verify:True/False,默认为True,认证SSL证书开关

  • cert:保存本地ssl证书路径

r = reuqests.get(url)

构造一个向服务器请求资源的Request对象(Request)

返回一个包含服务资源的Response对象(Response)

主要参数列表

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

  • url:你获取页面的url连接
  • params: url中的额外参数,字典或字节流格式,可选
  • *kwargs:12个控制访问的参数(同requests.request()函数除params*外的字段)
'''源码'''
def get(url, params=None, **kwargs): 
    r"""Sends a GET request.

    :param url: URL for the new :class:`Request` object.
    :param params: (optional) Dictionary, list of tuples or bytes to send
        in the query string for the :class:`Request`.
    :param \*\*kwargs: Optional arguments that ``request`` takes.
    :return: :class:`Response <Response>` object
    :rtype: requests.Response
    """

    kwargs.setdefault('allow_redirects', True)
    return request('get', url, params=params, **kwargs)

**Response对象:**包含爬虫返回的全部内容

>>> import requests
>>> r = requests.get("http://www.baidu.com")
>>> print(r.status_code)
200
>>> type(r)
<class 'requests.models.Response'>
>>> r.headers
{'Cache-Control': 'private, no-cache, no-store, proxy-revalidate, no-transform', 'Connection': 'keep-alive', 'Content-Encoding': 'gzip', 'Content-Type': 'text/html', 'Date': 'Wed, 18 Dec 2019 03:34:09 GMT', 'Last-Modified': 'Mon, 23 Jan 2017 13:27:29 GMT', 'Pragma': 'no-cache', 'Server': 'bfe/1.0.8.18', 'Set-Cookie': 'BDORZ=27315; max-age=86400; domain=.baidu.com; path=/', 'Transfer-Encoding': 'chunked'}
Response对象的属性
属性说明
r.status_codehttp请求的返回状态,200表示连接成功,404表示失败
r.texthttp响应内容的字符串形式,即url对应的页面内容
r.encoding从http header中猜测响应内容编码方式
r.apparent_encoding从内容中分析出的相应内容编码方式(备选编码方法)
r.contentHttp相应内容的二进制形式
code=200
code=404或其他
请求
r.status_code
r.text ,r.encoding,r.apparent_encoding
某些原因出错将产生异常
>>> r.encoding
'ISO-8859-1'
>>> r.apparent_encoding
'utf-8'
>>> r.encoding='utf-8'
>>> r.text
理解Response的编码
属性说明
r.encoding从http header中猜测响应内容编码方式
r.apparent_encoding从内容中分析出的相应内容编码方式(备选编码方法)

r.encoding:如果header中不存在charset,则认为编码为’ISO-8859-1’

r.apparent_encoding:根据网页内容分析出的编码方式

Requests库的head(url,**kwargs)方法

**kwargs与requests.request方法完全一样

>>> r = requests.head('http://httpbin.org/get')
>>> r.headers
{'Access-Control-Allow-Credentials': 'true', 'Access-Control-Allow-Origin': '*', 'Content-Encoding': 'gzip', 'Content-Type': 'application/json', 'Date': 'Wed, 18 Dec 2019 05:04:05 GMT', 'Referrer-Policy': 'no-referrer-when-downgrade', 'Server': 'nginx', 'X-Content-Type-Options': 'nosniff', 'X-Frame-Options': 'DENY', 'X-XSS-Protection': '1; mode=block', 'Connection': 'keep-alive'}
>>> r.text
''
Resquests库的Post(url,data=None,json=None,**kwargs)方法
  • url:拟更新页面的url
  • data:字典、字节序列或文件,Request的内容
  • JSON格式的数据,Request的内容
  • **kwargs:11个访问控制参数(同requests.request()的控制访问参数去除data和json)
>>> payload = {'key1':'value1','key2':'value2'}
>>> r = requests.post('http://httpbin.org/post',data=payload)
>>> print(r.text)
{
  "args": {}, 
  "data": "", 
  "files": {}, 
  "form": {
    "key1": "value1", 
    "key2": "value2"
  }, 
  "headers": {
    "Accept": "*/*", 
    "Accept-Encoding": "gzip, deflate", 
    "Content-Length": "23", 
    "Content-Type": "application/x-www-form-urlencoded", 
    "Host": "httpbin.org", 
    "User-Agent": "python-requests/2.22.0"
  }, 
  "json": null, 
  "origin": "124.88.99.162, 124.88.99.162", 
  "url": "https://httpbin.org/post"
}

向URL POST一个字典,自动编码为form(表单)

>>> r = requests.post('http://httpbin.org/post',data = 'ABC')
>>> print(r.text)
{
  "args": {}, 
  "data": "ABC", 
  "files": {}, 
  "form": {}, 
  "headers": {
    "Accept": "*/*", 
    "Accept-Encoding": "gzip, deflate", 
    "Content-Length": "3", 
    "Host": "httpbin.org", 
    "User-Agent": "python-requests/2.22.0"
  }, 
  "json": null, 
  "origin": "124.88.99.162, 124.88.99.162", 
  "url": "https://httpbin.org/post"
}

向URL POST一个字符串,自动编码为data

Requests库的put(url,data=None,**kwargs)方法
  • url:拟更新页面的url连接
  • data:字典、字节序列或文件,Request的内容
  • **kwargs:12个控制访问的参数
>>> payload = {'key1':'value1','key2':'value2'}
>>> r = requests.put('http://httpbin.org/put',data=payload)
>>> print(r.text)
{
  "args": {}, 
  "data": "", 
  "files": {}, 
  "form": {
    "key1": "value1", 
    "key2": "value2"
  }, 
  "headers": {
    "Accept": "*/*", 
    "Accept-Encoding": "gzip, deflate", 
    "Content-Length": "23", 
    "Content-Type": "application/x-www-form-urlencoded", 
    "Host": "httpbin.org", 
    "User-Agent": "python-requests/2.22.0"
  }, 
  "json": null, 
  "origin": "124.88.99.162, 124.88.99.162", 
  "url": "https://httpbin.org/put"
}
requests.patch(url,data=None,**kwargs)
  • url:拟更新页面的url连接
  • 字典、字节序列或文件,Request的内容
  • **kwargs:12个控制访问参数
request.delete(url,**kwargs)
  • url拟删除页面的url连接
  • **kwargs:13个控制访问的参数

理解Requests库的异常

Request对象

异常说明
requests.ConnectionError网络连接错误异常,如DNS查询失败、拒绝连接等
requests.HTTPErrorHTTP错误异常
requests.URLRequiredURL缺失异常
requests.TooManyRedirects超过最大重定向次数,产生重定向异常
requests.ConnectTimeout远程连接服务器超时
requests.Timeout请求URL超时,产生超时异常

Response对象

异常说明
r.raise_for_status()如果不是200,产生异常requests.HTTPError

**异常演示:**爬取网页通用代码框架

import requests
def getHTMLText(url):
	try:
        r = requests.get(url,timeout=30)
        r.raise_for_status() # 如果状态不是200,引发HTTPError异常
        r.encoding = r.apparent_encoding
        return r.text
    except:
        return "产生异常"

if __name__ == "__main__":
    url = "http://www.baidu.com"
    print(getHTMLText(url))

小结:Request库入门

一共有七个方法

requests.request()

requests.get()

requests.head()

requests.post()

requests.put()

requests.patch()

requests.delete()

爬取网页的通用代码框架

try:
    r = requests.get(url,timeout=30)
    r.raise_for_status() #  如果返回的对象状态码不是200,产生一次异常
    r.encoding = r.apparent_encoding
    return r.text
except:
    return "产生异常"

网络爬虫引发的问题

网络爬虫的尺寸

爬取网页 玩转网页爬取网站 爬取系列网站爬取全网
小规模,数据量小 爬取速度不敏感 占90%中规模,数据规模较大 爬取速度敏感大规模,搜索引擎 爬取速度关键
Request库Scrapy库定制开发

网络爬虫的“骚扰”!!!

受限于编写水平和目的,网络爬虫将会为web服务器带来巨大的资源开销

网络爬虫的法律风险!!!

服务器上的数据有产权归属

网络爬虫获取数据后牟利将带来法律风险

网络爬虫泄露隐私

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

网络爬虫限制

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

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

Robots协议

Robots :Robots Exclusion Standard 网络爬虫排除标准

作用:网站告知网络爬虫哪些页面可以抓取,哪些不行。

形式:在网站的根目录下的robots.txt文件。写明哪些目录是允许爬取的,哪些不行

案例:京东的Robots协议

User-agent: * 
Disallow: /?* 
Disallow: /pop/*.html 
Disallow: /pinpai/*.html?* 
User-agent: EtaoSpider 
Disallow: / 
User-agent: HuihuiSpider 
Disallow: / 
User-agent: GwdangSpider 
Disallow: / 
User-agent: WochachaSpider 
Disallow: /
Robots协议基本语法
# 注释
*代表所有
/代表根目录
User-agent:*
Disallow:/

百度Robots协议

新浪新闻Robots协议

qq的Robots协议

qq新闻Robots协议

国家教育部Robots协议,无robots协议

如果一个网站没有提供robots.txt,默认这个网站允许所有爬虫爬取其页面。

Robots协议的使用

网络爬虫:自动或人工识别robots.txt,再进行内容爬取。

约束性:Robots协议是建议但非约束性,网络爬虫可以不遵守,但存在法律风险。

对Robots协议的理解
爬取网页 玩转网页爬取网站 爬取系列网站爬取全网
访问量很小:可以遵守
访问量较大:建议遵守
非商业且偶尔:建议遵守
商业利益:必须遵守
必须遵守
小结

Robots协议基本语法

网络爬虫,要亦有道

实例

一:京东商品信息爬取

import requests
url = 'https://item.jd.com/100008384344.html'
try:
    r = requests.get(url)
    r.raise_for_status()
    r.encoding = r.apparent_encoding
    print(r.text[:1000])
expect:
    print("爬取失败")

二、亚马逊商品页面爬取

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])
expect:
    print("爬取失败")

三、百度/360搜索关键词提交

百度

import requests
keyword = 'Python'
try:
    kv = {'wd',keyword}
    r = requests.get("http://www.baidu,com/s",params = kv)
    print(r.request.url)
    r.raise_for_status()
    r.encoding = r.apparent_encoding
    print(len(r.text))
expect:
    print("爬取失败")

360

import requests
keyword = 'Python'
try:
    kv = {'q',keyword}
    r = requests.get("http://www.so,com/s",params = kv)
    print(r.request.url)
    r.raise_for_status()
    r.encoding = r.apparent_encoding
    print(len(r.text))
expect:
    print("爬取失败")

四、网络图片的爬取和存储

网络图片连接的格式

http://www.example.com/picture.jpg

国家地理网站

http://www.nationalgeographic.com.cn

选择一个图片web页面

http://www.ngchina.com.cn/photography/photo_of_the_day/3921.html

http://image.nationalgeographic.com.cn/2017/0211/20170211061910157.jpg

import requests
import os
url = "http://image.nationalgeographic.com.cn/2017/0211/20170211061910157.jpg"
root = "D://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("文件已存在")
expect:
    print("爬取失败")

五、IP地址归属地的自动查询

IP138

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:])
expect:
    print("爬取失败")
    

单元小结

五个实例:

Beautiful Soup 库的安装

官方地址

安装 在cmd命令行中 输入

pip install beautifulsoup4

测试

演示HTML页面地址:http://python123.io/ws/demo.html

>>> import requests
>>> r = requests.get('http://python123.io/ws/demo.html')
>>> demo = r.text
>>> from bs4 import BeautifulSoup
>>> soup = BeautifulSoup(demo,'html.parser')
>>> print(soup.prettify())

BeautifulSoup 库的基本元素

BeautifulSoup库的理解

BeautifulSoup库是解析、遍历、维护“标签树”的功能库

<p>..<p><!--标签Tag-->
<p class='title'>...</p>
<!--p:名称name,成对出现;class 属性atttributes 0个或多个>

BeautifulSoup库,也叫beautifulsoup4或bs4。

最长引用方式

from bs4 import BeautifulSoup # 从bs4中引入一个BeautifulSoup类
import bs4

HTMLDocument<---------------------->标签树<------------------->BeautifulSoup类

>>> from bs4 import BeautifulSoup
>>> soup = BeautifulSoup("<html>data</html>","html.parser")
>>> soup = BeautifulSoup(open("D://demo.html"),"html.parser")

BeautifulSoup库解析器

解析器使用方法条件
bs4的HTML解析器BeautifulSoup(mk,‘html.parser’)安装bs4库
lxml的HTML解析器BeautifulSoup(mk,‘lxml’)pip install lxml
lxml的XML解析器BeautifulSoup(mk,‘xml’)pip install lxml
html5lib的解析器BeautifulSoup(mk,‘html5lib’)pip install html5lib
BeatufulSoup类的基本元素
基本元素
Tag标签,最基本的信息组织单元,分别用<>和</>标明开头和结尾
Name标签的名字,

的名字是‘p’,格式:.name
Attributes标签的属性,字典形式组织,格式:.attrs
NavigableString标签内非属性字段,<>…</>中字符串,格式:.string
Comment标签内字符串的注释部分,一种特殊的Comment类型
基于bs4库的HTML内容遍历方法
<html>
<head>
<body>
<p>1
<p>2
<b>
<a>1
<a>2

下行遍历、上行遍历、平行遍历

标签树的下行遍历
属性说明
.content子节点的列表,将所有儿子节点存入列表
.children子节点的迭代类型,域content类似,用于循环遍历儿子节点
.descendants子孙节点的迭代类型,包含所有子孙节点,用于循环遍历
>>> soup = BeautifulSoup(demo,'html.parser')
>>> soup.head
>>> soup.head.contents
>>> soup.body.contents
标签树的上行遍历
属性说明
.parent节点的父亲标签
.parents节点先辈标签的迭代类型,用于循环遍历先辈节点
>>> soup.parent
>>> soup = BeautifulSoup(demo,'html.parser')
>>> for parent in soup.a.parents:
	if parent is None:
		print(parent)
	else:
		print(parent.name)

		
p
body
html
[document]

标签树的平行遍历

属性说明
.next_sibling返回按照HTML文本顺序的下一个平行节点标签
.previous_sibling返回按照HTML文本顺序的上一个平行节点标签
.next_siblings迭代类型,返回按照HTML文本顺序后续所有平行节点标签
.previous_siblings迭代类型,返回按照HTML文本顺序的前序所有平行节点标签

注意:平行遍历发生在同一个父节点下的个节点间

注意2:平行标签和儿子标签可能存在NavigableString类型的值(字符串)

for sibling in soup.a.next_siblings:
    print(sibling)
 for sinling in soup.a.previous_siblings:
    print(sibling)
    

基于bs库的HTML格式输出

print(soup.prettify())在每一个标签后添加换行符

bs4库的编码:采用utf-8

>>> soup  = BeautifulSoup("<p>中文</p>","html.parser")
>>> soup.p.string
'中文'
>>> print(soup.p.prettify())
<p>
 中文
</p>

小结

BeautifulSoup库入门:基本元素:Tag Name Attributes NavigableString Comment

遍历功能:.content .children .descendants .parent .parents///next_sibling .previous_sibling .next_siblings .previsou_siblings

信息标记的三种形式

信息的标记

一个信息: ‘北京理工大学’

一组信息 ‘北京理工大学’、‘北京市海淀中关村’、1949

标记: name:‘北京理工大学’、address:‘北京市海淀中关村’

  1. 标记后的信息可形成信息组织结构,增加了信息维度
  2. 标记后的信息可用于通讯、存储或展示
  3. 标记的结构和信息一样具有重要价值
  4. 标记后的信息更利于程序理解和运用

HTML和其他的信息标记

HTML是WWW(World Wide Web)的信息组织方式。(声音、图像、视频等超文本)

HTML通过预定于的<>…</>标签形式组织不同类型的信息

信息标记主要有三种形式:xml、json、yaml

XML(eXtensible、Markup、Language)

<img src = "china.jpg" size="10">...</img>
<!--名字    属性                   内容  名称-->

<<img src = "china.jpg" size="10"/>
<!--缩写形式-->

JSON (Javascript Object Notation)

有类型的键值对key:value,如

“name”:“北京理工大学”,
"year":1949
"names":["北京理工大学","延安自然科学院"]
"nameObject":{"newName""北京理工大学","oldName":"延安自然科学院"}

Yaml(Aint’t Markup Language)

无类型键值对 name:北京理工大学

通过缩进形式表达所属关系,-表示并列关系,|表达整块数据,#表示注释

name:
	-北京理工大学
	-延安自然科学学院
text: |#学校介绍
石河子大学是国家“211工程”重点建设高校、国家西部重点建设高校和省部共建高校,是“中西部高校基础能力建设工程”和“中西部高校综合实力提升工程”(一省一校)入选高校。2017年学校入选国家“双一流”建设一流学科建设高校,2018年入选“部省合建”高校,纳入教育部直属高校排序。 

三种信息标记比较

XML 最早的通用信息标记语言,可扩展性好,但繁琐。

JSON:信息有类型,适合程序处理(js),较XML简洁。

YAML:信息无类性,文本信息比例最高,可读性好

应用方向:

XML:Internet上的信息交互与传递。

JSON:移动应用云端和节点的信息通讯,无注释。

YAML:各类系统的配置文件,有注释,易读。

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

<>.find_all(name,sttrs,recursive,string,**kwargs)

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

name:对标签名称的检索字符串

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

recursive:是否对子孙全部检索。默认Ture。

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

简写形式:

  • (…)等价于.find_all(…)
  • soup(…)等价于soup.find_all(…)
扩展方法
方法说明
<>.find()搜索且只返回一个结果,字符串类型,同.find_all()参数
<>.find_parents()在先辈节点中搜索,返回列表类型,同.find_all()参数
<>.find_parent()在先辈节点中返回一个结果,字符串类型,同.find()参数
<>.find_next_siblings()后续平行节点中搜索,返回列表类型,同.find_all()参数
<>.find_next_sibling()后续平行节点中搜索返回一个结果,字符串类型,同.find_all()参数
<>.find_previous_siblings()在前序平行节点中搜索,返回列表类型,同.find_all()参数
<>.find_previous_sibling()在前序平行节点中搜索返回一个结果,字符串类型,同.find_all()参数

小结

信息标记与提取方法

XML、JSON、YAML

信息提取方法一般方法、等价形式和七个扩展方法

实例

实例一、“中国大学排名定向爬虫”实例介绍

功能描述:

输入:大学排名URL连接最好大学网

输出:大学排名信息的屏幕输出(排名、大学名称、总分)

技术路线:requests+bs4

定向爬虫:仅对输入url进行爬取,不扩展爬取

程序结构设计

步骤1:从网络上获取大学排名网页内容

​ getHTMLText()

步骤2:提取网页内容中信息到合适的数据结构

​ fillUnnivList()

步骤3:利用数据结构展示并输出结果

​ printUnivList()

import requests
from bs4 import BeautifulSoup
import bs4


def getHTMLText(url):
    try:
        r = requests.get(url, timeout=30)
        r.raise_for_status()
        r.encoding = r.apparent_encoding
        return r.text
    except:
        return ""
    return ""


def fillUnivList(ulist, html):
    soup = BeautifulSoup(html)
    for tr in soup.find("tbody").children:
        if isinstance(tr, bs4.element.Tag):
            tds = tr("td")  # find_all()的简写形式
            ulist.append([tds[0].string, tds[1].string, tds[3].string])
    pass


def printUnivList(ulist, num):
    print("{:^10}\t{:^26}\t{:^10}".format("排名", "学校名称", "总分"))
    for i in range(num):
        u = ulist[i]
        print("{:^10}\t{:^26}\t{:^10}".format(u[0], u[1], u[2]))
    print("Suc" + str(num))


def main():
    uinfo = []
    url = "http://www.zuihaodaxue.com/zuihaodaxuepaiming2016.html"
    html = getHTMLText(url)
    fillUnivList(uinfo, html)
    printUnivList(uinfo, 20)  # 20 univs


main()

优化:中文对齐问题的解决

采用中文字符的空格填充chr(12288)

正则表达式库Re入门

正则表达式的概念

regular expression regex RE

正则表达式是用来简洁表达一组字符串的表达式

如:‘PN’,‘PYN’,‘PYTN’,‘PYTHN’,‘PYTHON’,<======>正则表达式P(Y|YT|YTH|YTHO)?N

‘PY’,‘PYY’,‘PYYY’,…,‘PYYYY…’<=========>PY+

'PY’开头,后续存在不多于10个字符 ,后续字符不能是‘P’或‘Y’<===>PY[^PY]{0,10}

通用的字符串表达框架

用途:

  • 表达文本类型的特征
  • 同时查找或替换一组字符串
  • 匹配字符串的全部或部分区域

编译:将符合正则表达式语法的字符串转换成增则表达式,编译后的结果与要表达的字符串(组)

正则表达式的语法

P(Y|YT|YTH|YTHO)?N:正则表达式由字符和操作符构成

正则表达式的常用的操作符
操作符说明实例
.表示任何单个字符
[]字符集,对单个字符给出取值范围[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}表示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’、‘PYTHONNNN’…
PY[TH]ON‘PYTON’、‘PYHON’
PY[^TH]?ON‘PYON’、‘PYaON’、‘PYbON’、‘PYCON’、‘PYdON’、…
PY{:3}n‘PN’、‘PYN’、‘PYYN’、‘PYYYN’
经典的正则表达式
正则表达式意义
1+$由26个字母组成的字符串
2+$由26个字母和数字组成的字符串
^-?\d+$整数形式的字符串
3*[0-9][0-9]*$正整数形式的字符串
[1-9]\d{5}中国境内邮政编码,6位
[\u4e00-\u9fa5]匹配中文字符
\d{3}-\d{8}|\d{4}-\d{7}国内电话号码,010-68913536

案例

匹配IP地址的正则表达式

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

Re库的基本使用

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

调用方式
import Re
表示类型:
  1. raw string类型(原生字符串类型)

    如:Re库采用raw string 类型表示正则表达式,表示为r’text’

    原生字符串和字符串所不同的是只需要在字符串前加一个小写的r

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

  2. string类型,更繁琐

建议当正则表达式中有转义字符的时候,使用raw string类型

主要功能函数
函数说明
re.search()在一个字符串中搜索匹配正则表达式的第一个位置,返回match对象
re.match()从一个字符串的开始位置起匹配正则表达式,返回match对象
re.findall()搜索字符串,以列表类型返回全部能匹配的字符串
re.split()将一个字符串按照正则表达式匹配结果进行分割,返回列表类型
re.finditer()搜索字符串,返回一个匹配结果的迭代类型,每一个迭代元素是match对象
re.sub()在一个字符串中替换所有匹配正则表达式的字符串,返回替换后的字符串
re.search(pattern,string,flags)

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

  • pattern:正则表达式的字符串或原生字符串表示
  • string:待匹配字符串
  • flags:正则表达式使用时的控制标记

常用flags标记主要有

常用标记说明
re.I re.IGNORECASE忽略正则表达式的大小写,[A-Z]能够匹配小写字符
re.M re.MULTILINE正则表达式中的^操作符能够将给定字符串的每行当作匹配开始
re.S re.DOTALL正则表达式中的.操作符能够匹配所有字符,默认匹配除换行外的所有字符
>>> import re
>>> match = re.search(r'[1-9]\d{5}','BIT110081')
>>> if match::
	print(match.group(0))

	
110081
>>>    
re.match(pattern,string,flags)

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

  • pattern:正则表达式的字符串或原生字符串表示
  • string:待匹配字符串
  • flags:正则表达式使用时的控制标记
re.findall(pattern,string,flags)

搜索字符串,以列表类型返回全部能匹配的字符串

  • pattern:正则表达式的字符串或原生字符串表示
  • string:待匹配字符串
  • flags:正则表达式使用时的控制标记
re.split(pattern,string,maxsplit,flags)

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

  • pattern:正则表达式的字符串或原生字符串表示
  • string:待匹配字符串
  • maxsplit:最大分割数,剩余部分最为一个整体输出
  • flags:正则表达式使用时的控制标记
re.finditer(pattern,string,flags)

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

  • pattern:正则表达式的字符串或原生字符串表示
  • string:待匹配字符串
  • flags:正则表达式使用时的控制标记
re.sub(pattern,repl,string,count=0,flags=0)

在一个字符串中替换所有匹配正则表达式的字符串,返回替换后的字符串

  • pattern:正则表达式的字符串或原生字符串表示
  • repl:替换匹配字符串的字符串
  • string:待匹配字符串
  • count:匹配的最大替换次数
  • flags:正则表达式使用时的控制标记
Re库的另一种等价用法
>>>rst = re.search(r'[1-9]\d{5}','BIT 100081') # 函数式用法:一次性操作
'''面向对象用法:编译后的多次操作'''
>>>pat = re.compiler(r'[1-9\d{5}]')
>>>rst = pat.search(BIT 100081)

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

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

  • pattern:正则表达式的字符串或原生字符串表示
  • flags:正则表达式使用时的控制标记
等价方法对应
函数说明
regex.search()在一个字符串中搜索匹配正则表达式的第一个位置,返回match对象
regex.match()从一个字符串的开始位置起匹配正则表达式,返回match对象
regex.findall()搜索字符串,以列表类型返回全部能匹配的字符串
regex.split()将一个字符串按照正则表达式匹配结果进行分割,返回列表类型
regex.finditer()搜索字符串,返回一个匹配结果的迭代类型,每一个迭代元素是match对象
regex.sub()在一个字符串中替换所有匹配正则表达式的字符串,返回替换后的字符串

注:参数中不再需要正则表达式字符串

match对象

match对象的属性

属性说明
.string待匹配的文本
.re匹配时使用的pattern对象(正则表达式对象)
.pos正则表达式搜索文本的开始位置
.endpos正则表达式搜索文本的结束位置
match对象的常用方法
方法说明
.group(0)获得匹配后的字符串
.start()匹配字符串在原始字符串的开始位置
.end()匹配字符串在原始字符串的结束位置
.span()返回(.start(),.end())

Re库的贪婪匹配和最小匹配

实例

>>> match = re.search(r'PY.*N','PYANBNCNDN')
>>> match.group(0)
'PYANBNCNDN'
贪婪匹配:

Re库默认采用贪婪匹配,即输出匹配最长的子串。

最小匹配:

如何输出最短的字符串呢?

>>> match = re.search(r'PY.*?N','PYANBNCNDN')
>>> match.group(0)
'PYAN'

最小匹配操作符

操作符说明
*?前一个字符0次或无限次扩展,最小匹配
+?前一个字符1次或无限次扩展,最小匹配
??前一个字符0次或1次扩展,最小匹配
{m,n}?扩展前一个字符m至n次(含n),最小匹配

实例

淘宝商品比价定向爬虫实例介绍

功能描述:
  • 目标:获取淘宝搜索页面的信息,提取其中的商品名称和价格。
  • 理解:
    • 淘宝搜索接口
    • 翻页的处理
  • 技术路线:requests-re
程序的结构设计
  • 步骤1:提交商品搜索请求,循环获取页面。
  • 步骤2:对于每一个页面,提取商品名称和价格信息。
  • 步骤3:将信息输出到屏幕上。
import requests
import re


def getHTMLText(url):
    try:
        r = requests.get(url, timeout=30)
        r.raise_for_status()
        r.encoding = r.apparent_encoding
        return r.text
    except:
        return ""


def parsePage(ilt, html):
    try:
        plt = re.findall(r'\"view_price\"\:\"[\d\.]*\"', html)
        tlt = re.findall(r'\"raw_title\"\:\".*?\"', html)
        for i in range(len(plt)):
            price = eval(plt[i].split(':')[1])
            title = eval(tlt[i].split(':')[1])
            ilt.append([price, title])
    except:
        print("解析异常")
    print("")


def printGoodsList(ilt):
    tplt = "{:4}\t{:8}\t{:16}"
    print(tplt.format("序号", "价格", "商品名称"))
    count = 0
    for g in ilt:
        count = count + 1
        print(tplt.format(count, g[0], g[1]))
    print("")


def main():
    goods = '书包'
    depth = 2
    start_url = "https://s.taobao.com/search?q=" + goods
    infoList = []
    for i in range(depth):
        try:
            url = start_url + '&s=' + str(44 * i)
            html = getHTMLText(url)
            parsePage(infoList, html)
        except:
            continue
    printGoodsList(infoList)


main()

当前淘宝网需要先登录才能搜索

Scrapy网络爬虫框架

scrapy安装

执行:

pip install scrapy

scrapy不是一个简单的python库,是一个网络爬虫框架

爬虫框架定义

爬虫框架是实现爬虫功能的一个软件结构和功能组集合。

爬虫框架是一个半成品,能够帮助用户实现专业网络爬虫

Scrapy爬虫框架结构

"5+3"结构

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-39lEyfeT-1669043941495)(C:\Users\admin\Desktop\scrapy.PNG)]

其中 ENGINE、SCHEDULER、DOWNLOADER是已有实现

SPIDERS、ITEM PIPELINES需要用户编写(配置)

Scrapy爬虫框架解析

Engine 模块(不需要用户修改)
  • 控制所有模块之间的数据流
  • 根据条件触发事件
Downloader根据请求下载网页(不需要用户修改)
Scheduler 对所有爬虫爬取请求进行调度管理(不需要用户修改)

DownLoader Middleware

  • 目的:实施Engine、Scheduler和DownLoader之间进行用户可配置的控制

  • 功能:修改、丢弃、新增请求或响应

Spider(需要用户编写代码)
  • 解析Downloader返回的响应(Response)
  • 产生爬取网页(scraped item)
  • 产生额外的爬取请求(Request)
Item Pipelines
  • 以流水线方式处理Spider产生的爬取项(需要用户编写代码)
  • 由一组操作顺序组成,类似流水线,每一个操作时ItemPipeline类型
  • 可能操作包括:清理、检验、和查重爬取项的HTML数据、将数据存储到数据库。
Spider Middleware(用户可以编写配置代码)
  • 目的:对请求和爬取项的载处理
  • 修改、丢弃、新增请求或爬取项

requests库和scrapy对比

相同点:

两者都可以进行页面请求和爬取,Python爬虫的两个技术路线

两者可用性都好,文档丰富,入门简单

两者都没有处理js、表单提交、验证码等功能(可扩展)。

不同点
requestsScrapy
页面级爬虫网站级爬虫
功能库框架
并发行考录不足,性能较差并发性好,性能较高
尊重点在于页面下载重点在于爬虫结构
定制灵活一眼定制灵活,深度定制困难
上手十分简单入门稍难
选用那个技术路线开发爬虫
  • 非常小的需求,request库。
  • 不太小的需求,Scrapy框架
  • 定制程度很高的需求(不考虑规模),自搭框架,request>Scrapy

Scrapy常用命令

Scrapy命令行

scrapy命令行是为持续运行设计的专业爬虫框架,提供操作的Scrap命令行。

Scrapy命令行格式

> scrapy [options][args]

命令说明格式
startproject创建一个新工程scrapy startprojrct <name> [dir]
genspider创建一个爬虫scrapy genspider [options]<name><domain>
settings获得一个爬虫scrapy settings[options]
crawl运行一个爬虫scrapy crawl<spider>
list列出工程中所有爬虫scrapy list
shell启动URL调试命令行scrpy shell[url]

Scrapy爬虫的命令行逻辑

为什么Scrapy采用命令行创建和运行爬虫

  • 命令行(不是图形界面)更容易自动化,适合脚本控制
  • 本质山,Scrapy是给程序员使用的,功能(而不是界面)更重要。

实例

演示HTML地址页面爬取

产生步骤:

  1. 步骤1:建议一个Scrapy爬虫工程

    scrapy startproject python123demo
    

    生成的工程文件目录结构

    ─python123demo      ------>外层目录
        │  scrapy.cfg   ------>部署Scrapy爬虫的配置文件
        │
        └─python123demo ------>Scrapy框架的用户自定义Python代码
            │  items.py       ---->Items代码模板(继承类)
            │  middlewares.py ----->Middlewares代码模板(继承类)
            │  pipelines.py    ----->Pipelines代码模板(继承类)
            │  settings.py		----->Scrapy怕成的配置文件
            │  __init__.py   ---->初始化脚本
            │
            ├─spiders    ----->Spiders代码模板目录(继承类)
            │  │  __init__.py   ---->初始文件,虚无修改
            │  │
            │  └─__pycache__    ----->缓存目录,无需修改
            └─__pycache__    
    
  2. 步骤2:在工程中产生Scrapy爬虫

scrapy genspider demo python123.io

在spiders目录下多出一个demo.py文件

# -*- coding: utf-8 -*-
import scrapy


class DemoSpider(scrapy.Spider):
    name = 'demo'
    allowed_domains = ['python123.io']
    start_urls = ['http://python123.io/']

    def parse(self, response):
        pass

parse用于处理响应,解析内容形成字典,发现新的URL爬取请求

  1. 步骤3:配置产生的spider爬虫

    # -*- coding: utf-8 -*-
    import scrapy
    
    
    class DemoSpider(scrapy.Spider):
        name = 'demo'
        # allowed_domains = ['python123.io']
        start_urls = ['http://python123.io/ws/demo.html']
    
        def parse(self, response):
    	fname = response.url.split('/')[-1]
    	with open (fname,'wb') as f:
    		f.write(response.body)
        self.log("Saved file %s." %name)
    
    

    完整版

    # -*- coding: utf-8 -*-
    import scrapy
    
    
    class DemoSpider(scrapy.Spider):
        name = 'demo'
        def start_request(self):
            urls = ['http://python123.io/ws/demo123.html']
            for url in urls:
                yield scrapy.Request(url=url, callback=self.parse)
        
    
        def parse(self, response):
    	fname = response.url.split('/')[-1]
    	with open (fname,'wb') as f:
    		f.write(response.body)
        self.log("Saved file %s." %name)
    
    
  2. 步骤4:运行爬虫,获取网页

yield关键字

yield<------------------>生成器

  • 生成器是一个不断产生值的函数
  • 包含yielde语句的函数是一个生成器
  • 生成器每次产生一个值(yield语句),函数被冻结,被唤醒后再产生一个值
生成器实例
# 生成器写法
>>> def gen(n):
	for i in range(n):
		yield i**2

>>> for i in gen(5):
	print(i,"  ",end="")

	
0   1   4   9   16 
# 普通写法

>>> def square(n):
	ls = [i**2 for i in range(n)]
	return ls
>>> for i in square(5):
	print(i,"  ",end="")

为何要有生成器?

生成器相比一次列车出所有内容的优势

  • 更节省存储空间
  • 响应更迅速
  • 使用更加灵活

Scrapy爬虫的基本使用

Scrapy爬虫的使用步骤

步骤1:创建一个工程和Spider模板

步骤2:编写spider

步骤3:编写Item Pipline

步骤4:优化配置策略

涉及三各类:

Request类、Response类、Item类

Request类

class scrapy.http.Request()

  • Request对象表示一个Http请求。
  • 由Spider生成,由Downloader执行

六个常用的属性和方法

属性或方法说明
.urlRequest对应请求的URL地址
.method对应请求的方法,‘GET’、‘POST’等
.headers字典类型风格的请求头
.body请求内容主体,字符串类型
.meta用户添加的扩展信息,在Scrapy内部模块建传递信息使用
.copy()复制该请求
Response类

class scrapy.http.Response()

  • Response兑现表示一个HTTP响应。

  • 由Downloader生成,由Spider处理

    七个常用的属性或方法

属性或方法说明
.urlResponse对应的URL地址
.statusHTTP状态吗,默认200
.headersResponse对应的头部信息
.bodyResponse对应的内容信息,字符串类型
.flags一组标记
.request产生Response类型对应的Resquest对象
.copy()复制该响应
Item类

class scrapy.item.Item()

  • Item对象表示一个从HTML页面中提取的内容信息
  • 由Spider生成,由Item PipeLine处理
  • Item类似字典类型,可以按照字典类型操作。

Scrapy爬虫提取信息的方法

scrapy爬虫支持使用多种HTML信息提取方法

  • Beautiful Soup
  • lxml
  • re
  • XPath Selector
  • CSS Selector
CSS Selector基本使用
.css('a::attr(href)').extract()

CSS Selector由W3C组织维护并规范


为何要有生成器?

生成器相比一次列车出所有内容的优势

- 更节省存储空间
- 响应更迅速
- 使用更加灵活



## Scrapy爬虫的基本使用

### Scrapy爬虫的使用步骤

步骤1:创建一个工程和Spider模板

步骤2:编写spider

步骤3:编写Item Pipline

步骤4:优化配置策略

### 涉及三各类:

Request类、Response类、Item类

#### Request类

class scrapy.http.Request()

- Request对象表示一个Http请求。
- 由Spider生成,由Downloader执行

六个常用的属性和方法

| 属性或方法 | 说明                                               |
| ---------- | -------------------------------------------------- |
| .url       | Request对应请求的URL地址                           |
| .method    | 对应请求的方法,‘GET’、‘POST’等                    |
| .headers   | 字典类型风格的请求头                               |
| .body      | 请求内容主体,字符串类型                           |
| .meta      | 用户添加的扩展信息,在Scrapy内部模块建传递信息使用 |
| .copy()    | 复制该请求                                         |

#### Response类

class scrapy.http.Response()

- Response兑现表示一个HTTP响应。

- 由Downloader生成,由Spider处理

  七个常用的属性或方法

| 属性或方法 | 说明                               |
| ---------- | ---------------------------------- |
| .url       | Response对应的URL地址              |
| .status    | HTTP状态吗,默认200                |
| .headers   | Response对应的头部信息             |
| .body      | Response对应的内容信息,字符串类型 |
| .flags     | 一组标记                           |
| .request   | 产生Response类型对应的Resquest对象 |
| .copy()    | 复制该响应                         |

#### Item类

class scrapy.item.Item()

- Item对象表示一个从HTML页面中提取的内容信息
- 由Spider生成,由Item PipeLine处理
- Item类似字典类型,可以按照字典类型操作。

### Scrapy爬虫提取信息的方法

scrapy爬虫支持使用多种HTML信息提取方法

- Beautiful Soup
- lxml
- re
- XPath Selector
- CSS Selector

#### CSS Selector基本使用

<HTML>.css('a::attr(href)').extract()

CSS Selector由W3C组织维护并规范


  1. A-Za-z ↩︎

  2. A-Za-z ↩︎

  3. 0-9 ↩︎

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

天码村

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值