爬虫

发布于 2020-05-13  1144 次阅读


宗旨:网站即是API

0. 准备工作

1. robots 协议

爬取网站一定要遵守robots.txt,否则会有法律风险(面向监狱编程)。在网站的域名后加 /robots.txt 可以看到其对爬虫的限制。比如:
百度:https://www.baidu.com/robots.txt
B站:https://www.bilibili.com/robots.txt
帝国理工:http://www.imperial.ac.uk/robots.txt

2. 安装python环境——Anaconda傻瓜式安装

3. 安装库文件pip install requests 装完后运行以下代码测试:

import requests

r = requests.get("http://www.baidu.com/")
r.raise_for_status()
print(r.status_code) #HTTP 状态码

r.encoding = "utf-8"

print(r.text)   #打印网站内容

4. 安装HTML解析库pip install beautifulsoup4 装完后运行以下代码测试:

import requests
from bs4 import BeautifulSoup

url = "https://python123.io/ws/demo.html"

pa = {'User-Agent': 'Mozilla/5.0'}

r = requests.get(url, headers = pa)
r.raise_for_status()

r.encoding = r.apparent_encoding
demo = r.text

soup = BeautifulSoup(demo,"html.parser")

print(soup.prettify())

5. 安装 scrapy 框架pip install scrapy

若报超时错误,更换为国内源即可,代码如下:pip install scrapy -i http://pypi.douban.com/simple --trusted-host pypi.douban.com

安装完成后,在命令行运行 scrapy -h 测试。

6. 了解生成器——yield

def gen(n):
    for i in range(n):
        yield i**2
        
for i in gen(6):
    print(i,end = " ")
#[out]: 0 1 4 9 16 25 

生成器的优势:

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

1. requests库

0. requests库的7个主要方法

除去第一个,其余的与HTTP请求相对应

  1. requests.request():构造一个请求,支撑以下各方法的基础方法
  2. requests.get():获取HTML网页的主要方法
  3. requests.head():获取HTML网页的头部信息
  4. requests.post():向HTML网页提交POST请求
  5. requests.put():向HTML网页提交PUT请求
  6. requests.patch():向HTML网页提交局部修改请求
  7. requests.delete():向HTML网页提交删除请求

1. requests.get("url")

r = requests.get("url")

返回Response对像(即上面代码中的r)的属性:

  • r.status_code:HTTP请求的返回状态;详情点这里
  • r.text:HTTP响应内容的字符串形式——url对应的页面内容
  • r.encoding:响应内容的编码方式(header中分析出)
  • r.apparent_enconding:备选编码方式(内容中分析出)
  • r.content:响应内容的二进制形式

r.encoding 不能正确编码时,尝试使用 r.apparent_enconding 的编码方式

爬取网页的通用代码框架

import requests

def getHTMLText(url):
    try:
        r = requests.get(url)
        r.raise_for_status()
        r.encoding = r.apparent_encoding
        
        return r.text
    except:
        return "ERROR--getHTMLText"

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

2. requests实战

1. 爬取京东商品页面

要修改HTTP请求的headers信息,变成浏览器访问。可以把以下代码放进一个函数,方便使用

import requests

url = "https://item.jd.com/100008771796.html"

kv = {'User-Agent': 'Mozilla/5.0'}

try:
    r = requests.get(url, headers = kv)
    r.raise_for_status()
    r.encoding = r.apparent_encoding
        
    print(r.text[:1000])
except:
    print(r.request.headers)
    print(r.status_code)
    print("ERROR--getHTMLText")

2. 百度搜索关键词提交

import requests

url = "http://www.baidu.com/s"

pa = {'User-Agent': 'Mozilla/5.0'}
kv = {'wd': 'python'}

try:
    r = requests.get(url, headers = pa, params = kv)
    r.raise_for_status()
    r.encoding = r.apparent_encoding
        
    print(r.text[:1500])
    print(r.request.headers)
except:
    print(r.request.headers)
    print(r.status_code)
    print("ERROR--getHTMLText")

3. 网络图片的爬取和储存(也可以是视频和gif)

import requests

url = "https://car3.autoimg.cn/cardfs/product/g30/M00/96/DF/1024x0_1_q95_autohomecar__ChsEf16Qj0CAZge6AA0PAzXq4SI349.jpg"
path = "C:/Users/Ran/Desktop/abc.jpg" #文件储存路径+文件名+文件格式

r = requests.get(url)
print(r.status_code)

with open(path,'wb') as f:
    f.write(r.content)

4. IP归属地查询

查询IP地址的网站的url会时常更新,以下代码可能失效。若不成功,更换最新的url即可。

import requests

url = "https://www.ip138.com/iplookup.asp?ip="
ip = "117.157.165.230"
url += ip
url += "&action=2"

pa = {'User-Agent': 'Mozilla/5.0'}

r = requests.get(url, headers = pa)
print(r.status_code)

r.encoding = r.apparent_encoding

print(r.text)

3. BeautifulSoup 库

1. 有4种解析器,最常用的是 ‘html.parser’。其他三种需要安装对应的库

在运行本小节其他代码前,请先运行以下代码,然后复制其他代码到命令窗口运行,或者将其他代码复制到这段代码下运行整个程序。

import requests
from bs4 import BeautifulSoup

url = "https://python123.io/ws/demo.html"

pa = {'User-Agent': 'Mozilla/5.0'}

r = requests.get(url, headers = pa)
r.raise_for_status()

r.encoding = r.apparent_encoding
demo = r.text

soup = BeautifulSoup(demo,"html.parser")

2. 5种基本元素

  • Tag:标签——最基本的信息组织单位;以<>开头,以</>结尾
  • Name:标签的名字;<p>...</p>的名字是‘p’,格式:<tag>.name
  • Attributes:标签的属性;以字典形式组织,格式:<tag>.attrs
  • NavigableString:标签内非字符串的属性;<>...</>中的字符,格式:<tag>.string
  • Comment:标签内字符串的注释部分,一种特殊的comment类型
soup.title
Out[33]: <title>This is a python demo page</title>

tag = soup.a

print(tag)
<a class="py1" href="http://www.icourse163.org/course/BIT-268001" id="link1">Basic Python</a>

soup.a.name
Out[36]: 'a'

soup.a.parent.name
Out[37]: 'p'

soup.a.parent.parent.name
Out[38]: 'body'

soup.a.parent.parent.parent.name
Out[39]: 'html'

tag.attrs
Out[40]: 
{'href': 'http://www.icourse163.org/course/BIT-268001',
 'class': ['py1'],
 'id': 'link1'}

tag.attrs['id']
Out[41]: 'link1'

type(tag)
Out[42]: bs4.element.Tag

tag.string
Out[43]: 'Basic Python'

soup.p.string
Out[44]: 'The demo python introduces several python courses.'

3. HTML文件遍历方法

标签树的下行遍历

  • .contents:返回子节点的列表,将<tag>所有子节点存入列表
  • .children:子节点的迭代类型,与 .contents 类似,用于循环编译子节点
  • .descendants:子孙节点的迭代类型,包含所有子孙节点,用于循环遍历
for child in soup.body.children:
    print(child)

标签树的上行遍历

  • .parent:节点的父亲标签
  • .parents:节点先辈标签的迭代类型,用于循环遍历先辈节点
for parent in soup.a.parents:
    if parent is None:
        print(parent)
    else:
        print(parent.name)

标签书的平行遍历

  • .next_sibling:返回下一个平行节点的标签
  • .previous_sibling:返回上一个平行节点的标签
  • .next_siblings:迭代类型,返回之后所有的平行节点的标签
  • .previous_siblings:迭代类型,返回之前所有的平行节点的标签

平行遍历必须在同一父节点下的各节点之间

soup.a.next_sibling
Out[56]: ' and '

soup.a.next_sibling.next_sibling
Out[57]: <a class="py2" href="http://www.icourse163.org/course/BIT-1001870001" id="link2">Advanced Python</a>

soup.a.previous_sibling
Out[58]: 'Python is a wonderful general-purpose programming language. You can learn Python from novice to professional by tracking the following courses:\r\n'

for sibling in soup.a.next_siblings:
    print(sibling.name)
    
None
a
None

4. 让 HTML 的内容更友好的显示

.prettify()

print(soup.a.prettify())
<a class="py1" href="http://www.icourse163.org/course/BIT-268001" id="link1">
 Basic Python
</a>

5. 信息提取的一般方法

<>.find_all(name, attrs, recursive, string, **kwargs)——以列表的形式返回查找的结果

  • name:对标签名称的检索字符串
  • attrs:对标签属性值的检索字符串
  • recursive:是否对子孙全部检索(默认为True)
  • string:<>...</>中字符串区域的检索字符串

以下代码在console中运行

import re #正则表达式库
for tag in soup.find_all(re.compile('b')):
    print(tag.name)
Out[4]:  
body
b

soup.find_all(id=re.compile('link'))
Out[5]: 
[<a class="py1" href="http://www.icourse163.org/course/BIT-268001" id="link1">Basic Python</a>,
 <a class="py2" href="http://www.icourse163.org/course/BIT-1001870001" id="link2">Advanced Python</a>]

soup.find_all(string = re.compile('python'))
Out[6]: 
['This is a python demo page',
 'The demo python introduces several python courses.']

简写/等价

  • <tag>(...) == <tag>.find_all(...)
  • soup.(...) == soup.find_all(...)

.find() 还有7中扩展用法,这里就不一一列出了。