Python爬虫基础

什么是网络爬虫

 网络爬虫可以按照指定的规则(网络爬虫的算法)自动浏览或抓取网络中的信息,通过Python可以很轻松地编写爬虫程序或者是脚本。

 如百度的爬虫,它的名字叫做百度蜘蛛,它是百度搜索引擎的一个自动程序。它每天都会在海量的互联网信息中进行爬取,收集并整理互联网上的网页、图片视频等信息。然后当用户在搜索引擎中输入对应的关键字时,百度将从收集的网络信息中找出相关的内容,按照一定的顺序将信息呈现给用户。

网络爬虫的基本原理

一个通用的网络爬虫基本工作流程如下; 在这里插入图片描述

  1. 获取初始的URL,该URL地址是用户自己指定的初始爬取的网页;
  2. 爬取对应URL地址的网页时,获取新的URL地址;
  3. 将新的URL地址放入URL队列中;
  4. 从URL队列中读取新的URL,然后依据新的URL爬取网页,同时从新的网页中获取新的URL地址,重复上述的爬取过程。
  5. 设置停止条件,即在满足停止条件时,停止爬取。

网络爬虫的网络请求

 下面给出三种Python实现HTTP网络请求最常见的3中方式:urllib、urllib3、requests.

  1. urllib模块
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# get请求获取百度网页内容
import urllib.request # 导入模块
# 打开指定需要爬取的网页
response=urllib.request.urlopen("http://www.baidu.com")
html=response.read()
print(html)

# post请求实现获取网页信息
import urllib.request
import urllib.parse
# 将数据用urlencode编码处理后,再使用encoding设置为utf-8编码
data=bytes(urllib.parse.urlencode({'word':'hello'}),encoding='utf-8')
# 打开指定需要爬取的网页
response=urllib.request.urlopen("http://httpbin.org/post",data=data)
html=response.read()
print(html)
  1. urllib3模块,它用于http客户端的python库
1
2
3
4
5
6
7
8
9
10
# get实现发送网络请求
import urllib3
# 创建poolmanager对象,用于处理与线程池的连接以及线程安全的所有细节
http=urllib3.PoolManager()
# 对索要爬取的对象发送请求
response=http.request('get','http://blog.csdn.net')
print(response.data)
# # post实现获取网络信息的内容
response=http.request('post','http://httpbin.org/post',fields={'hello':'world'})
print(response.data)
  1. requests模块
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# get请求
import requests

response=requests.get('http://blog.csdn.net')
print(response.status_code)
print(response.url)
print(response.headers)
print(response.headers)
print(response.history)
print(response.cookies)
print(response.encoding)
# get请求
import requests
data={'world':'hello'}
# 发送请求
response=requests.get('http://httpbin.org/post',data=data)
print(response.content)

Requests库

 requests是python的第三方模块,它也是目前公认的爬取网页最好的python第三方库,它很简洁,它的信息可以从http://www.python-request.org获取;

 Requests库的get()方法

1
r=requests.get(url)

 它构造一个向服务器请求资源的Request,返回一个包含服务器资源的Response对象,response包含了网页爬虫爬取返回的所有内容;完整形式如下 requests.get(url,params=None,**kwargs)

 params为常用参数,**kwargs为其他控制访问访问参数;

 下面给出requests的get方法的源代码

1
2
3
def get(url, params=None, **kwargs):
kwargs.setdefault('allow_redirects',True)
return request('get',url,params=params,**kwargs)

 我们发现,它的实现用到了requests的request方法,实际上,requests库提供了7个方法,然而包含get的其他六个类似方法的实现,都用到了request方法,

 response的status_code用来检测请求的状态码,如果状态码是200说明访问成功,而非200则表明访问失败;response的常用属性如下:

属性 说明
r.status_code http请求的返回状态,200表示连接成功,404表示失败
r.text http响应内容的字符串形式,即url对应的内容
r.encoding 从http header中猜测的响应内容编码方式
r.apparent_encoding 从内容中分析出的响应内容编码方式
r.content http响应内容的二进制形式

 其中,r.encoding是从http的header中猜测内容编码方式,它不是向r.apparent_encoding一样根据内容进行分析编码方式,它相对来说是不准确的,我们要获取网页内容时,我们将它们输出到控制台上,有时编码不正确,可能会获取中文的乱码,所以我们一般需要看encoding与apparent_encoding是否相同;

request方法

1
requests.request(method,url,**kwargs)

method:请求方式

1
2
3
4
5
6
7
8
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)
r=requests.request('GET',url,**kwargs)
1
2
3
4
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

 它添加了一个字典,被添加到url中,服务器接受这些参数,并根据这些参数筛选一些数据;

 对于**kwargs来说,它有13种:

pagrams,data,json,headers,cookies,auth,files,timeout,proxies,allow_redirects,stream,vertify,cert.

爬取网页的通用代码框架

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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://blog.csdn.net"
print(getHTMLText(url))

网络爬虫引用的问题

 网络爬虫的尺寸

规模 爬取对象
小规模,数据量小,爬取速度不敏感,Requests库>90% 爬取网页,玩转网页
中规模,数据规模较大,爬取速度敏感,Scrapy库 爬取网站,爬取系列网站
大规模,搜索引擎,爬取速度关键,如百度、Google,定制开发 爬取全网

 带来的问题 - 服务器骚扰:服务器默认按人数约定访问能力,爬虫的访问速度比人快的多,将带来对服务器的额外开销; - 法律风险:服务器上的数据有产权归属,爬虫爬取数据进行牟利将带来法律风险; - 隐私泄露:爬虫可以爬取一些网站上的私人信息,它可以破解简单的访问控制能力,获得保护数据从而泄露个人隐私;

Robot协议

 Robot协议是来告知网络爬虫,该网站哪些页面可以爬取,哪些不可爬取,它在网站的根目下,有些网站没有Robot协议,代表任何爬虫可以爬取任何数据;

 如京东:https://www.jd.com/.robots.txt

 它包含两部分User-Agent表示哪些爬虫,Disallowe代表不可爬取的资源;

 网络爬虫自动或人工识别robots.txt,在进行内容爬取;网络其实可以不遵守Robots协议,但是存在法律风险

请求headers处理

 有时在请求一个网页内容时,无论发现是get还是post以及其他请求,都会出现status_code非200的错误,这种错误多为服务器拒绝了您的访问,那是因为这些网页为了防止恶意采集信息,采用了反爬虫设置,即浏览器根据请求的头部信息判断这个请求是浏览器还是一段爬虫程序发送来的;这时候可以通过模拟浏览器的头部信息来访问;如下:

1
2
3
4
5
6
import requests

kv={'User-Agent':'Mozilla/5.0'}
response=requests.get('http://www.amazon.cn/',headers=kv)
response.encoding=response.apparent_encoding
print(response.text)

搜索引擎关键词提交接口

百度的关键词接口 http://www.baidu.com/s?wd=keyword 360的关键词接口 http://www.so.com/s?q=keyword 实现

1
2
3
4
5
import requests
kv={'wd':'Python'}
r=requests.get('http://www.baidu.com/s',params=kv)
print(r.request.url)
# ->http://www.baidu.com/s?wd=Python

网络图片的爬取

https://wx4.sinaimg.cn/mw690/8d05b653ly1g4n0elm7axj20j60b475b.jpg

1
2
3
4
5
6
7
8
9
import requests
path='D://abc.jpg' # 保存在D盘下,以abc.jpg命名
url='https://wx4.sinaimg.cn/mw690/8d05b653ly1g4n0elm7axj20j60b475b.jpg'
# 以原文件名命名
# roo='D://'
# path=root+url.split('/')[-1]
r=requests.get(url)
with open(path,'wb') as f:
f.write(r.content)

 首先打开一个文件,将url返回的内容写到这个路径中,r.content返回的是二进制形式;

 类似的还有视频,动画、音频文件等等的爬取

IP地址查询

1
2
3
4
5
import requests
url="http://m.ip138.com/ip.asp?ip="
r.requests.get(url+'202.204.80.112')
r.encoding=r.apparent_coding
print(r.text[-500:])

BeautifulSoup的使用

BeautifulSoup的使用
1
2
3
from bs4 import BeautifulSoup
# 第一个参数是html,第二个是解析器
soup=BeautifulSoup('<p>data</p>','html,parser')

在一个html文件中,对应一个标签树,而一个标签树对应一个BeautifulSoup类,即一个BeautifuLSoup对应应一个HTML/XML文档的全部内容;

1
2
3
from bs4 import BeautifulSoup
soup=BeautifulSoup('<html>data</html>','html.parser')
soup2=BeautifulSoup(open('D://demo.html'),'html.parser')
获取一个html网页源码

1
2
3
4
5
6
7
8
import requests
from bs4 import BeautifulSoup

url='https://python123.io/ws/demo.html'
demo=requests.get(url).text
soup=BeautifulSoup(demo,'html.parser')
# print(soup.contents)
print(soup.prettify())
BeautifulSoup库解析器
在这里插入图片描述
BeautifulSoup类的基本元素

在这里插入图片描述 这里需要注意的是,对于beautiful.tag来说,比如soup.a它是只返回html中的第一个a标签内的内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
import requests
from bs4 import BeautifulSoup

url='https://python123.io/ws/demo.html'
demo=requests.get(url).text
soup=BeautifulSoup(demo,'html.parser')
print(soup.title)
tag=soup.a
print(tag)
print(tag.name) # a标签的名字
print(tag.parent.name) # a标签的父亲
print(tag.parent.parent.name)# a的父亲的父亲
print(tag.attrs)
print(tag.attrs['class'])
print(tag.attrs['href'])
print(type(tag)) # <class 'bs4.element.Tag'>
print(tag.string)
print(soup.p.string)
print(type(soup.p.string)) # <class 'bs4.element.NavigableString'>
newsoup=BeautifulSoup('<b><!--This is a comment--></b><p>This is not a comment</p>','html.parser')
print(newsoup.b.string)
print(type(newsoup.b.string)) # <class 'bs4.element.Comment'>
print(newsoup.p.string)
print(type(newsoup.p.string)) # <class 'bs4.element.NavigableString'>

# -><title>This is a python demo page</title>
# -><a class="py1" href="http://www.icourse163.org/course/BIT-268001" id="link1">Basic Python</a>
# ->a
# ->p
# ->body
# ->{'href': 'http://www.icourse163.org/course/BIT-268001', 'class': ['py1'], 'id': 'link1'}
# ->['py1']
# ->http://www.icourse163.org/course/BIT-268001
# -><class 'bs4.element.Tag'>
# ->Basic Python
# ->The demo python introduces several python courses.
# -><class 'bs4.element.NavigableString'>
# ->This is a comment
# -><class 'bs4.element.Comment'>
# ->This is not a comment
# -><class 'bs4.element.NavigableString'>

标签树的下行遍历

在这里插入图片描述 在这里插入图片描述

1
2
for child in soup.body.children:
print(child)

标签树的上行遍历

在这里插入图片描述
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import requests
from bs4 import BeautifulSoup

url='https://python123.io/ws/demo.html'
demo=requests.get(url).text
soup=BeautifulSoup(demo,'html.parser')
for parent in soup.a.parents: # a为一种标签类型,对先辈标签遍历
if parent is Node:
print(parent)
else:
print(parent.name)
# ->p
# ->body
# ->html
# ->[document]
标签树的平行遍历
在这里插入图片描述
平行遍历发生在同一个父节点下的个节点间
1
2
3
4
for sibling in soup.a.next_siblings:# 遍历后序节点
print(sibling)
for sibling in soup.a.previous_siblings:# 遍历前续节点
print(sibling)
爬取天猫Ipad商品信息
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
import requests
import re
import json

def getHtml(url) :
try:
reponse = requests.get(url)
if reponse.status_code != 200:
return None
else :
return reponse.text
except:
return None

def getInf(html):
pattern =re.compile('<div class="product.*?<a href="(.*?)".*?</b>(.*?)</em>.*?title="(.*?)".*?<a.*?>(.*?)</a>.*?</div>', re.S)
items = re.findall(pattern, html)
for item in items:
yield {
'href':item[0],
'price':item[1],
'title':item[2],
'shop':item[3]
}

def writeFile(content):
with open('3.txt', 'a', encoding = 'utf-8') as f:
f.write(json.dumps(content, ensure_ascii = False) + '\n')
f.close()

def main():
url ='https://list.tmall.com/search_product.htm?q=ipad&type=p&vmarket=&spm=875.7931836%2FB.a2227oh.d100&from=mallfp..pc_1_searchbutton'
html = getHtml(url)
for item in getInf(html):
print(item)
writeFile(item)

if __name__ == '__main__':
main()

这里自己第一次亲自,根据html,写出了正则表达式。 待续···