Python网络爬虫与信息提取

文章目录

单元一 Requests库

HTTP协议及Requests库方法

Python网络爬虫与信息提取
HTTP协议:Hypertext Transfer Protocal,超文本传输协议,基于响应与请求、无状态的应用层协议

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

每个URL格式:http://host[:port][path]
host:一个合法的Internet主机域名或IP地址
port:端口号,缺省为80
path:请示资源的路径
URL通过HTTP协议存取资源的Internet路径,一个URL对应一个资源

HTTP协议方法:
Python网络爬虫与信息提取
Python网络爬虫与信息提取
PATCH和PUT的区别:
若URL有一组数据UserInfo,包含UserID、UserName等20个字段,要修改UserName,其他不变,PATCH仅向URL提交UserName的局部更新请示,PUT必须要提交所有字段,未提交的字段被删除。所以PATCH方法可以节省带宽
Python网络爬虫与信息提取
Requests库的post()方法

payload = {'key1':'value1','key2':'value2'}
r = requests.post('http://httpbin.org/post',data = payload)
print(r.text)

键值对默认存到表单下

r = requests.post('http://httpbin.org/post',data = 'ABC')
print(r.text)

字符串自动编码为data
put()方法会覆盖原有的数据

Requests库get()方法

url = 某个网址
r = requests.get(url)

request()构造一个向服务器请求资源的Requests对象
Response返回一个包含服务器资源的Requests对象

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

url:拟获取页面的url链接
params:url中的额外参数,字典或字节流格式,可选
**kwargs:12个控制访问的参数
Requests库共有7个常用方法,除了requests()方法为基础方法外,其他方法都是调用requests()方法

import requests
r = requests.get("http://www.baidu.com")
print(r.status_code)
type(r)
r.headers

状态码为200表示成功
Response对象的属性
Python网络爬虫与信息提取
Python网络爬虫与信息提取

import requests
r = requests.get("http://www.baidu.com")
r.status_code
r.encoding
r.apparent_encoding
r.encoding = 'utf-8'
r.text

如果header中不存在charset,encoding则认为编码为ISO-8859-1,它不能解析中文

Requests库的安装

cmd控制台:pip install requests

Requests主要方法

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

method:请求方法,如get/put/OPTIONS等7种
url:链接
**kwargs:控制访问参数,13个

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)

**kwargs:
params:字典或字节序列,作为参数增加到url中

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

data:字典、字节序列或文件对象,作为Request的内容

kv = {'key1':'value1','key2':'value2'}
r = requests.request('GET','http://python123.io/ws',data=kv)
body ='主体内容'
r = requests.request('POST','http://python123.io/ws',data = body)

json:JSON格式的数据,作为Reuqest的内容

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('data.xls', 'rb')}
r = requests.request('POST','http://python123.io/ws',files= fs)

timeout:设定超时时间,以秒为单位

r = requests.request('GET','http://python123.io/ws',timeout=10)

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)

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

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

**kwargs:12个参数,即request中除了params外的其他参数

requests.head(url,**kwargs)

**kwargs:13个参数

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

data:字典、字节序列或文件,Request的内容
json:JSON格式的数据,Request的内容
**kwargs:11个参数

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

data:字典、字节序列或文件,Request的内容
**kwargs:12个参数

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

data:字典、字节序列或文件,Request的内容
**kwargs:12个参数

requests.delete(url,**kwargs)

**kwargs:13个参数

爬取网页的通用代码框架

Requests库的异常
Python网络爬虫与信息提取
Python网络爬虫与信息提取

import requests
def geHTMLText(url):
    try:
        r = requests.get(url,timeout=30)
        r.raise_for_status()
        r.encoding = r.apparent_encoding
        return r.text
    except:
        return "产生异常"

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

Robots协议

Robots Exclusion Standard 网络爬虫排除标准
robots.txt在网站的根目录下

*表示所有

Disallow: /?*表示所有都不允许访问以?开头的路径

Disallow:/pop/*.html表示所有都不允许访问pop下的所有html

User-agent:EtaoSpider
Disallow: /
表示EtaoSpider不允许爬取任何内容

如果一个网址不提供robots协议,则默认允许所有爬虫爬取

robots协议遵守方式

robots协议是建议但非约束性,不遵守存在法律风险

类人行为可不参考Robots协议,访问量非常小,速度慢

网络爬虫引发的问题

小规模,可用requests库
中规模,数据规格大,速度敏感,可用Scrapy
爬取全网,如搜索引擎,定制开发

网络爬虫的骚扰,服务器崩溃

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

爬虫泄露隐私

爬虫限制:
来源审查:判断User-agent
robots协议:告示牌

京东商品页面的爬取

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

百度/360搜索关键词提交

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

网络图片的爬取和存储

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

IP地址归属地的自动查询

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

BeautifulSoup库安装

cmd下,输入pip install beautifulsoup4

html的格式
<html>
<head></head>
<body>
<\body>
</html>

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

Beautiful Soup库的基本元素

此库是解析、遍历、维护标签树的功能库

<p class=“title”>…</p>
名称是p
属性是class=“title”

解析器:
Python网络爬虫与信息提取
基本元素
Python网络爬虫与信息提取

from bs4 import BeautifulSoup
soup = BeautifulSoup(demo,"html.parser")
soup.title
tag = soup.a
tag.attrs
tag.attrs['class']
tag.attrs['href']
type(tag.attrs)
type(tag)
soup.a.name
soup.a.parent.name
soup.a.parent.parent.name
soup.a.string
soup.p
soup.p.string
type(soup.p.string)
newsoup = BeautifulSoup("<b>--Tis is a comment</b><p>This is not a comment</p>","html.parser")
newsoup.b.string
type(newsoup.b.string)
newsoup.p.string
type(newsoup.p.string)

基于bs4库的HTML格式输出

from bs4 import BeautifulSoup
soup = BeautifulSoup(demo,"html.parser")
soup.prettify()
print(soup.prettify())

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

Python网络爬虫与信息提取
标签树的下行遍历
Python网络爬虫与信息提取

soup = BeautifulSoup(demo,"html.parser")
soup.head
soup.head.contents
soup.body.contents
len(soup.body.contents)
soup.body.contents[1]

上行遍历
Python网络爬虫与信息提取

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

上行遍历

soup = BeautifulSoup(demo,"html.parser")
for parent in soup.a.parents:
	if parent is None:
		print(parent)
	else:
		print(parent.name)

平行遍历
Python网络爬虫与信息提取
平行遍历发生在同一父节点下的各节点间

soup = BeautifulSoup(demo,"html.parser")
soup.a.next_sibling
soup.a.next_sibling.next.sibling
soup.a.previous_sibling
soup.a.previous_sibling.previous_sibling
soup.a.parent

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

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

返回一个列表类型,存储结果
name:对标签名称的检索字符串
attrs:对标签属性值的检索字符串,可标注属性检索
recursive:是否对子孙全部检索,默认是True
string:<>…</>中字符串区域的检索字符串

soup.find_all('a')
soup.find_all(['a','b'])

若只给True,则返回所有标签

for tag in soup.find_all(True):
	print(tag.name)
soup.find_all('p','course')

精准查找:

soup.find_all(id='link1')

模糊查找:正则表达式

soup.find_all(string = "Basic Python")

简写:
<tag>()等价于<tag>.find_all()

扩展方法
Python网络爬虫与信息提取

三种信息标记形式的比较

XML:eXtensible Markup Language
用<>标记
<name>…</name>

JSON:JavaScript Object Notation
用有类型键值对标记
“key”:“value”
“key”:[“value1”,“value2”]
“key”:{“value1”,“value2”}

YAML:YAML Ain’t Markup Language
无类型键值对标记
firstName:xxx
lastName:XXX

XML:有效信息占比不高,大部分信息被标签占用,可用于Internet
JSON:比XML简洁,用于移动端或程序,无法体现注释
YAML:有效信息利用率最高,用于系统配置文件,有注释

提取信息的一般方法

一:完整解析后再提取关键信息
二:无视标记形式,直接搜索关键信息

from bs4 import BeautifulSoup
soup = BeautifulSoup(demo,"html.parser")
for link in soup.find_all('a'):
	print(link.get('href'))

中国大学排名定向爬虫

import requests
import bs4
from bs4 import BeautifulSoup

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 fillUnivList(ulist, html):
    soup = BeautifulSoup(html, "html.parser")
    for tr in soup.find('tbody').children:
        if isinstance(tr,bs4.element.Tag):
            tds = tr('td')
            ulist.append([tds[0].string,tds[1].string,tds[2].string])

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

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

main()

Re库的match对象

Match对象是一次匹配的结果,有type(match)将返回<class ‘_sre.SRE_Mathc’>
Match对象的属性
Python网络爬虫与信息提取
Match对象的方法
Python网络爬虫与信息提取

import re
m = re.search(r'[1-9]\d{5}','BIT10081 TSU100084')
m.string
m.re
m.pos
m.endpos
m.group(0)
m.start()
m.end()
m.span()

Re库的基本使用

用于字符串匹配
raw string类型(原生字符串类型),表达为r’text’,它不包含转义符的字符串,比如\\仅用\表示

Re库主要功能函数
Python网络爬虫与信息提取

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

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

Python网络爬虫与信息提取

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

match可以返回空,调用group会出错

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

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

import re
ls = re.findall(r'[1-9]\d{5}','BIT10081 TSU100084')
ls
re.split(pattern,string,maxsplit=0,flags=0)

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

import re
re.split(r'[1-9]\d{5}','BIT10081 TSU100084')
re.split(r'[1-9]\d{5}','BIT10081 TSU100084',maxsplit=1)
re.finditer(pattern,string,flags=0)

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

import re
for m in re.finditer(r'[1-9]\d{5}','BIT10081 TSU100084'):
	if m:
		print(m.group(0))
re.sub(pattern,repl,string,count=0,flags=0)

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

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

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

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

最小匹配

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

最小匹配操作符
Python网络爬虫与信息提取

正则表达式概念

regular expression
regex
re
正则表达式是用来简洁表达一组字符串的表达式
Python网络爬虫与信息提取
通用的字符串表达框架
简洁表达一组字符串的表达式
针对字符串表达“简洁”和“特征”思想的工具
判断某字符串的特征归属

正则表达式的语法

Python网络爬虫与信息提取
abc*:表示*前面的c可以出现0次或无限次,如ab,abc,abcc,…
abc+:表示+前面的c可以出现1次或无限次,如abc,abcc…
Python网络爬虫与信息提取

Python网络爬虫与信息提取
Python网络爬虫与信息提取
IP地址的正则表达式
Python网络爬虫与信息提取

淘宝商品比价定向爬虫

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(plt):
            price = eval(plt[i].split(':')[1])
            title = eval(title[i].split(':')[1])
            ilt.append([price,title])
    except:
        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]))

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

main()

股票数据定向爬虫

import requests
from bs4 import BeautifulSoup
import traceback
import re

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

def getStockList(lst, stockURL):
    html = getHTMLText(stockURL)
    soup = BeautifulSoup(html, 'html.parser')
    a = find_all('a')
    for i in a:
        try:
            href = i.attrs['href']
            lst.append(re.findall(r"[s][hz]\d{6})",href)[0])
        except:
            continue

def getStockInfo(lst, stockURL, fpath):
    for stock in lst:
        url = stockURL + stock + ".html"
        html = getHTMLText(url)
        try:
            if html == "":
                continue
            infoDict = {}
            soup = BeautifulSoup(html,'html.parser')
            stockInfo = soup.find('div',attrs={'class':'stock-bets'})
            
            name = stockInfo.find_all(attrs={'class':'bets-name'})[0]
            infoDict.update({'股票名称':name.text.split()[0]})

            keyList = stockInfo.find_all('dt')
            valueList = stockInfo.find_all('dd')
            for i in range(len(keyList)):
                key = keyList[i].text
                val = valueList[i].text
                infoDict[key] = val
             with open(fpath, 'a',encoding = 'utf-8') as f:
                 f.write(str(infoDict) + '\n')y
        except:
            traceback.print_exc()
            continue

def main():
    stock_list_url = 'http://quote.eastmoney.com/stocklist.html'
    stock_Info_url = 'http://gupiao.baidu.com/stock/'
    output_file = 'D://BaiduStockInfo.txt'
    slist = []
    getStockList(slist, stock_list_url)
    getStockInfo(slist, stock_info_url, output_file)

main()

requests库和Scrapy爬虫比较

相同点:
都可以进行页面请求和爬取
可用性好
没有处理js,提交表单

不同:
requests:页面级爬虫,功能库,并发性不足,页面下载,定制灵活,简单
Scrqpy:网站级爬虫,框架,并发性好,爬虫结构,深度定制困难,稍难

Scrapy爬虫的常用命令

Scrapy命令行格式

>scrapy<command>[options][args]

常用命令
Python网络爬虫与信息提取
命令行更容易自动化,适合脚本控制

本质上,Scrapy是给程序员用的,功能更重要

Scrapy爬虫框架介绍

爬虫框架是实现爬虫功能的一个软件结构和功能组件集合
爬虫框架是一个半成品,能帮助用户实现专业网络爬虫

Scrapy爬虫框架解析

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

Downloader Middleware:修改、丢弃、新增请求或响应
Spider:解析Downloader返回的响应;产生爬取项;产生额外的爬取请求
Item Pipelines:以流水线方式处理Spider产生的爬取项;由一组操作顺序组成,类似流水线,每个操作是一个Item Pipeline类型;清理、检验和查重爬取项中的HTML数据,将数据存储到数据库(需要用户编写)

Spider Middleware:修改、丢弃、新增请求或爬取项

Scrapy实例1

安装好Scrapy库后
打开命令提示符
输入d:
输入cd pycodes
输入scrapy startproject python123demo

scrapy.cfg:部署Scrapy爬虫的配置文件
python123demo:Scrapy框架的用户自定义Python代码
_init_.py:初始化脚本
items.py:Items代码模板(继承类)
middlewares.py:Middlewares代码模板(继承类)
pipelines.py:Pipelines代码模块(继承类)
settings.py:Scrapy爬虫的配置文件

输入scrapy genspider demo python123.io

增加了一个新的文件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爬取请求

修改demo.py

# -*- 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('Save file %s.' %name)

输入scrapy crawl demo

Scrapy爬虫的基本使用

创建一个工程和Spider模板
编写Spider
编写Item Pipeline
优化配置策略

Requests类
Python网络爬虫与信息提取
Response类
Python网络爬虫与信息提取
Item类

Scrapy爬虫提取信息的方法
BeautifulSoup
lxml
re
XPath Selector
CSS Selector

yield关键字的使用

生成器是一个不断产生值的函数
包含yield语句的函数是一个生成器
生成器每次产生一个值,函数被冻结,被唤醒后再产生一个值

def gen(n):
	for i in range(n):
		yield i**2

股票数据Scrapy爬虫实例

建立工程和Spider模板
编写Spider
编写ITEM Pipelines

输入:
scrapy startproject BaiduStocks
cd BaiduStocks
scrapy genspider stocks baidu.com

修改spiders/stocks.py

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

class StockSpider(scrapy.Spider):
	name = "stocks"
	start_urls = ['http://quote.eastmoney.com/stocklist.html']
	
	def parse(self, response):
		for href in response.css('a::attrs(href)').extract():
			try:
				stock = re.findall(r"[s][hz]\d{6}",href)[0]
				url = 'https://gupiao.baidu.com/stock/' + stock + '.html'
				yield scrapy.Request(url, callback=self.parse_stock)
			except:
				continue
	
	def parse_stock(self, response):
		infoDict = {}
		stockInfo = response.css('.stock-bets')
		name = stockInfo.css('bets-name').extract[0]
		keyList = stockInfo.css('dt').extract()
		valueList = stockInfo.css('dd').extract()
		for i in range(keyList):
			key = re.findall(r'>.*</dt>',keyList[i])[0][1:-5]
			try:
				val = re.findall(r'\d+\.?.*</dd>',valueList[i])[0][0:-5]
			except:
				val ='--'
			infoDict[key]=val
		infoDict,update(
			{'股票名称':re.findall('\s.*\(',name)[0].split()[0] + \
			re.findall('\>.*\<',name)[0][1:-1]})
		yield infoDict

修改pipelines.py文件

class BaidustockPipeline(object):
	def process_item(self, item, spider):
		return item
	
class BaidustocksInfoPipeline(object):
	def open_spider(self,spider):
		self.f = open('BaiduStockInfo.txt','w')
	def close_spider(self, spider):
		self.f.close()
	
	def process_item(self, item, spider):
		try:
			line = str(dict(item) + '\n'
			self.f.write(line)
		except:
			pass
		return item

修改settings.py,找到一个参数ITEM_PIPELINES

ITEM_PIPELINES = {
	'BaiduStocks.pipelines.BaidustocksInfoPipeline':300,
}

执行:scrapy crawl stocks

上一篇:python:使用beautiful soup库解析html、xml页面


下一篇:Python爬取MacWk网站最新软件列表(软件简介、链接、下载次数)