网络爬虫与信息提取

0x00 Requests库

Requests库的主要方法
方法说明
requests.request()构造一个请求(主方法)
requests.get()获取HTML网页
requests.head()获取网页头信息
requests.post()向网页提交POST请求
requests.put()向网页提交PUT请求
requests.patch()向网页提交局部修改请求
requests.delete()向网页提交删除请求
Response对象的属性
属性说明
r.status_codeHTTP请求的返回状态
r.textHTTP响应内容的字符串形式
r.encodingheader中猜测的响应内容编码方式
r.apparent_encoding从内容分析出的响应内容编码方式
r.contentHTTP响应内容的二进制形式
Requests库的异常
异常说明
requests.ConnectionError网络连接错误异常
requests.HTTPErrorHTTP错误异常
requests.URLRequiredURL缺失异常
requests.TooManyRedirects重定向异常
requests.ConnectTimeout连接远程服务器超时异常
requests.Timeout请求URL超时异常
爬取网页的通用框架
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 = input("请输入要访问的url:")
    print(getHTMLText(url))

0x01 Robots协议

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

协议作用

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

协议形式

在网站根目录下的robots.txt文件

基本语法
User-agent:*
Disallow:/
遵循方式
  • 网络爬虫

    自动或人工识别robots.txt,在进行内容爬取

  • 约束性

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

  • 类人类行为可不参考Robots协议

0x02 Requests库实例

一、爬取京东商品信息
import requests

url = "https://item.jd.com/100006536488.html"
try:
    r = requests.get(url)
    r.raise_for_status()
    r.encoding = r.apparent_encoding
    print(r.text[:1000])
except:
    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)
except:
    print("爬取失败!")
三、360搜索提交关键字
import requests

keyword = 'python'
try:
    kv = {'q':keyword}
    r = requests.get("https://www.so.com/s",params=kv)
    r.raise_for_status()
    r.encoding = r.apparent_encoding
    print(r.text)
except:
    print("爬取失败!")
四、图片的爬取与存储
import requests
import os

url = "http://image.nationalgeographic.com.cn/2017/0211/20170211061910157.jpg"
root = "E://911208//网易云音乐//image//"
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("爬取失败")
五、IP地址归属地查询
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("爬取失败!")

0x03 Beautiful Soup库

Beautiful Soup库解析器
解析器使用方法条件
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
Beautiful Soup类的基本元素
基本元素说明
Tag标签
Name标签的名字
Attributes标签的属性
NavigableString标签内非属性字符串
Comment标签内字符串的注释部分
标签树的下行遍历
属性说明
.contents将tag所有儿子节点存入列表
.children与.contents类似,用于循环遍历儿子结点
.descendants子孙节点的迭代类型,包含所有子孙节点,用于循环遍历
标签树的上行遍历
属性说明
.parent节点的父亲标签
.parents节点先辈标签的迭代类型,用于循环遍历先辈节点
标签树的平行遍历
属性说明
.next_sibling返回下一个平行节点标签
.previous_sibling返回上一个平行节点标签
.next_siblings返回后序所有平行节点标签
.previous_siblings返回前序所有平行节点标签
信息提取的一般方法
  • 完整解析信息的标记形式,在提取关键信息
  • 无视标记信息,直接提取关键信息
  • 结合形式解析与搜索方法,提取关键信息

0x04 Beautiful Soup库实例

中国大学排名
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 ""

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):
    tplt = '{0:^10}\t{1:{3}^10}\t{2:^10}'
    print(tplt.format('排名', '学校名称', '地址', chr(12288)))
    for i in range(num):
        u = ulist[i]
        print(tplt.format(u[0],u[1],u[2],chr(12288)))

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

0x05 正则表达式

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

  • 通用字符串表达框架
  • 简洁表达一组字符串的表达式
  • 针对字符串表达简洁和特征思想的工具
  • 判断某字符串的特征归属

文本处理:

  • 表达文本类型的特征
  • 同时查找或替换一组字符串
正则表达式常用操作符
操作符说明实例
.表示任何单个字符
[ ]字符集,对单个字符给出取值范围[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次ab{1,2}c表示abc、abbc
^匹配字符串开头^abc表示abc且在一个字符串的开头
$匹配字符串结尾abc$表示abc且在一个字符串的结尾
( )分组标记,内部使用|操作符(abc|def)表示abc、def
\d数字,等价于[0-9]
\w单词字符,等价于[A-Za-z0-9]
经典正则表达式实例
^[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}  //国内电话号码

0x06 Re库

Re库是Python的标准库,主要用于字符串匹配,默认采用贪婪匹配,即输出匹配的最大字串。

如需最小匹配,可在操作符后加’?'表示输出最小匹配。

正则表达式的表示类型
  • raw string类型(原生字符串类型)
  • string类型(需要’‘转义’\d’等)
Re库的两种用法
  • 函数式用法:一次性操作

    rst = re.search(r'[1-9]\d{5}', 'BIT 100081')
    
  • 面向对象用法:编译后的多次操作

    pat = re.compile(r'[1-9]\d{5}')
    rst = pat.search('BIT 100081')
    
Re库主要功能函数
函数说明
re.search()在一个字符串中搜索匹配正则表达式的第一个位置,返回match对象
re.match()从一个字符串的开始位置起匹配正则表达式,返回match对象
re.findall()搜索字符串,以列表类型返回全部能匹配的子串
re.split()将一个字符串按照正则表达式匹配结果进行分割,返回列表类型
re.finditer()搜索字符串,返回一个匹配结果的迭代类型,每个迭代元素是match对象
re.sub()在一个字符串中替换所有匹配正则表达式的字串,返回替换后的字符串

0x07 Re库实例

一、淘宝商品比价定向爬虫
import requests
import re

def getHTMLText(url):
    try:
        header = {
            'User-Agent': 'Mozilla/5.0',
            'cookie': 'thw=cn; isg=BMHBPkhhe1tnBZdn4oW3NuPq0w3b7jXgVDwiVyMWnkgnCuHcaz6vsM8I6P5MGc0Y; cna=ERS7FiBJnl0CAXWWtw3DaILO; t=d20c989f94e109d35897baef41022d70; l=cBgQ3TRRQYql2zNCBOCNVuI8as79OIOYYuPRwN0Xi_5Nt6L1f97Oo5xcQFp6csWd9g8B4zotpap9-etkiDt6Qt--g3fP.; uc3=id2=VyyVyIaXGLNIDQ%3D%3D&nk2=F5RDKXHyOAaXshI%3D&lg2=W5iHLLyFOGW7aA%3D%3D&vt3=F8dBxdzwHz9KDFZYuFA%3D; lgc=tb654825781; uc4=nk4=0%40FY4I6gVSHObwqYGig5lEYrmSBfzs0A%3D%3D&id4=0%40VXtaRp7R7jWArSR8hervDYzL5XaI; tracknick=tb654825781; _cc_=UIHiLt3xSw%3D%3D; tg=0; mt=ci=7_1; enc=rZfi7zAhVsMoQi2YeTWL7H7gE%2BxulQNht0LC6l6eJwZuKa345jaCDE64p7WhiEQQsOVT7LZl4S4CCMxMEWeAMw%3D%3D; hng=CN%7Czh-CN%7CCNY%7C156; cookie2=157366bac664d23b6a09667d9ec41da5; v=0; _tb_token_=53b53bb70a5e7; uc1=cookie14=UoTUOLPGs7ssvw%3D%3D; JSESSIONID=4E2E3B16DCC8C679A015D932188B50BD; alitrackid=www.taobao.com; lastalitrackid=www.taobao.com'}
        r = requests.get(url, headers=header, timeout=30)
        r.raise_for_status()
        r.encoding = r.apparent_encoding
        return r.text
    except:
        return ""

def parsPage(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("")

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

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

main()
二、股票数据定向爬虫
import requests
from bs4 import BeautifulSoup
import traceback
import re

def getHTMLText(url, code="utf-8"):
    try:
        r = requests.get(url)
        r.raise_for_status()
        r.encoding = code
        return r.text
    except:
        return ""

def getStockList(lst, stockURL):
    html = getHTMLText(stockURL, "GB2312")
    soup = BeautifulSoup(html, 'html.parser') 
    a = soup.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):
    count = 0
    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' )
                count = count + 1
                print("\r当前进度: {:.2f}%".format(count*100/len(lst)),end="")
        except:
            count = count + 1
            print("\r当前进度: {:.2f}%".format(count*100/len(lst)),end="")
            continue

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

main()

0x08 Scrapy爬虫框架

Scrapy常用命令
命令说明
Scrapy startproject创建一个新工程
Scrapy genspider创建一个爬虫
Scrapy Settings获取爬虫配置信息
Scrapy crawl运行一个爬虫
Scrapy list列出工程中所有爬虫
Scrapy shell启动URL调试命令行
Scrapy爬虫的使用步骤
  • 创建一个工程和Spider模板
  • 编写Spider
  • 编写Item Pipeline
  • 优化配置策略
Scrapy爬虫的数据类型
  • Request类

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

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

    Response对象表示一个HTTP响应,由Downloader生成,由Spider处理

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

    Item对象表示一个从HTML页面中提取的信息内容,由Spider生成,由Item Pipeline

0x09 Scrapy爬虫实例:股票数据

stocks.py
# -*- coding: utf-8 -*-
import scrapy
import re


class StocksSpider(scrapy.Spider):
    name = "stocks"
    start_urls = ['http://quote.eastmoney.com/stocklist.html']

    def parse(self, response):
        for href in response.css('a::attr(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(len(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
# -*- coding: utf-8 -*-

# Define your item pipelines here
#
# Don't forget to add your pipeline to the ITEM_PIPELINES setting
# See: http://doc.scrapy.org/en/latest/topics/item-pipeline.html


class BaidustocksPipeline(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
BOT_NAME = 'BaiduStocks'

SPIDER_MODULES = ['BaiduStocks.spiders']
NEWSPIDER_MODULE = 'BaiduStocks.spiders'

ROBOTSTXT_OBEY = True

ITEM_PIPELINES = {   'BaiduStocks.pipelines.BaidustocksInfoPipeline': 300,
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值