分类

类型:
不限 游戏开发 计算机程序开发 Android开发 网站开发 笔记总结 其他
评分:
不限 10 9 8 7 6 5 4 3 2 1
原创:
不限
年份:
不限 2018 2019

技术文章列表

  • 基于DirectShow实现的视频捕捉与采集程序

    前言DirectShow是微软公司提供的一套在Windows平台上进行流媒体处理的开发包,与DirectX开发包一起发布。DirectShow为多媒体流的捕捉和回放提供了强有力的支持。用DirectShow开发应用程序,我们可以很方便地从支持WDM驱动模型的采集卡上捕获数据,并且进行相应的后期处理乃至存储到文件中。
    DirectShow是基于COM的,为了编写DirectShow应用程序,需要了解COM客户程序编写的基础知识。DirectShow提供了大量的接口,但在编程中发现还是不够方便,如果能构建一个视频捕捉类把常用的一些动作封装起来,那么就更方便了。
    编程思路为了更加容易建立视频捕捉应用程序,DirectShow提供了一个叫做Capture Graph Builder的对象,Capture Graph Builder提供IcaptureGraphBuilder2接口,该接口可以建立和控制Capture Graph。
    建立视频捕捉程序,必须首先获取并初始化IcaptureGraphBuilder2接口,然后选择一个适当的视频捕捉设备。选择好设备后,为该设备创建Capturefilter,然后调用AddFilter把Capture filter添加到Filter Graph。如果仅仅希望用摄像头来进行实时监控的话,只需要在上面的基础上调用ICaptureGraphBuilder2::RenderStream就可以了
    本程序在VS2010 PlatForm采用Micosoft 的DriectXShow9.0b SDK编写的一套实时视频捕捉,采集功能,并且还提供了视频和单帧图像参数设置界面,采集的视频流和单帧图像都写在文件中,之后使用OpenCV进行音视频的处理,当然这是我后期要做的事情,前面部分已经完成,前半部分开发使用了因为很多东西要查,需要学习DXShow如何设计filter graph manger 和filter graph 的创建以及pin 的连接,最后renderstream,产生视频流,之后,在视频流上捕捉单帧图像数据,加入图像头,写入文件进行保存到文件系统.
    环境配置
    首先保证已经正确安装了MicroSoft的DXSDK,请一定安装DriectXShow9.0b版本的SDK包,这里我将其安装了在C盘根目录下
    打开VS2010项目,在项目属性中设置项目需要使用的项目需要包含的包含目录和库目录
    具体步骤:

    项目-项目名属性-配置属性-VC++目录-包含目录-“C:\DXSDK\Include”项目-项目名属性-配置属性-VC++目录-包含目录-“C:\DXSDK\Lib”

    软件效果全局效果

    视频格式设置

    图像格式设置
    1 留言 2019-05-09 16:13:33 奖励16点积分
  • 网络爬虫技术原理介绍 精华

    什么是爬虫?网络爬虫是一种按照一定的规则,自动的爬取万维网信息的程序或者脚本。网络爬虫按照系统结构和实现技术,大致分为通用爬虫,聚焦爬虫,增量式爬虫,深层网络爬虫。但是实际应用中一般都是几种爬虫技术相结合实现的。
    搜索引擎其实是一种大型的复杂的网络爬虫属于通用爬虫的范畴。专业性质的搜索引擎则是属于聚焦爬虫的范畴。增量式爬虫则是对已下载的网页采取增量式更新和只爬取新产生的或者已经发生变化的网页的爬虫。Web网页按照存在的方式可以分为表层网页和深层网页,表层网页通常是指传统引擎可以索引的页面,以超链接可以到达的静态网页为主构成的Web页面;深层网络是那些大部分内容不能通过静态链接获取的、隐藏在搜索表单后的,只有用户提交一些关键词才能获取的Web页面。爬取深层网络页面的爬虫就属于深层网络爬虫的范畴。
    爬虫的工作流程

    首先选取一部分或者一个精心挑选的中字URL
    将这些URL放入带抓取的URL队列
    从待抓取的URL队列中读取待抓取的URL,解析DNS,并且得到主机的IP,并将URL对应的网页下载下来,存储到已下载的网页数据中。并且将这些URL放进已抓取的URL队列中
    分析已抓取URL队列中URL,从已下载的网页数据中分析出其他的URL,并和已抓取的URL进行去重处理,最后将去重后的URL放入待抓取URL队列,从而进入下一个循环

    爬虫的python实现框架Scrapy爬虫的python实现的库有很多,主要有urllib,httplib/urllib,requests.这里主要讲一下Scrapy框架。
    Scrapy使用python写的Crawler Framwork,简单轻巧,并且非常方便。Scrapy使用的是Twisted异步网络库来处理网络通信,架构清晰,并且包含了各种中间件接口,可以灵活的完成各种需求。


    引擎打开一个网站,找到处理改网站的spider并向该spider请求第一个要爬取的URL
    引擎从Spider中获取的第一个要爬取的URL并通过调度器(Scheduler)以Requests进行调度
    引擎向调度器(Scheduler)请求下一个要爬取的URL
    调度器(Scheduler)返回下一个要爬取的URL给引擎(Scrapy Engine),引擎将URL通过下载器中间件(Downloader Middleware)转发给下载器
    一旦页面下载完毕,下载器生成一个该页面的Response,并将其通过下载器中间件转发给引擎
    引擎从下载器中间件接收到Response并通过spider中间件发送给spider处理
    Spider处理Response并返回爬取到的Item和新的Request给引擎
    引擎将爬取到的item给Item Pipeline,将Request给调度器
    从第2步开始重复直到调度器中没有Request,引擎关闭该网站
    6 留言 2019-04-05 15:45:45 奖励15点积分
  • 基于Python实现的新闻网络爬虫程序 精华

    1、简介1.1 引用术语与缩写解释


    缩写、术语
    解 释




    Python
    一种简洁而强大的解释型脚本语言


    pyodbc
    Python下的ODBC数据库访问组件


    SQLAlchemy
    Python下的ORM数据访问组件


    pywin32
    Python下的Win32接口访问组件


    requests
    Python下的Web访问组件


    Pillow
    Python下的图像处理组件


    解释型语言
    无需编译源码可敏捷部署并执行的语言


    IOC
    控制反转,运行时可由数据反向干涉程序执行逻辑的设计模式


    RegularExpression
    正则表达式,查找和替换文本模式的简洁而灵活的表示法


    XML
    扩展标记语言,用于配置及数据交互



    1.2 概要本文档针对以下三个方面进行了详细说明:

    架构说明,对新闻网络爬虫的核心架构进行描述,供开发人员在开发或升级应用时参考
    部署说明,对新闻网络爬虫的部署步骤进行描述,供部署人员进行应用部署或升级时参考
    扩展说明,对新闻网络爬虫的扩展模式进行描述,供开发人员扩展爬虫功能时参考

    1.3 应用设计目标
    易于扩展
    易用
    易于维护

    为了达成这些设计目标我们采用了以下设计机制:

    易于扩展

    使用解释型、面向对象的Python语言实现程序逻辑:解释型语言易于扩展部署,结合抓取模块的IOC机制,更新升级时无需停机;面向对象易于代码复用,创建新爬虫模块时仅需少量重载代码修改控制流程IOC化,利用XML配置文件,可动态增减爬虫任务、控制爬虫任务启动条件、更改爬虫规则以及爬虫逻辑
    易用

    服务化,爬虫任务被封装为Windows 服务,可便捷地启动与停止,于后台执行长期运行
    易于维护

    编程语言简洁,缩进式风格减少了语法性代码,动态数据类型减少了声明性代码,函数式编程特性减少了重复性函数代码,友好而功能强大的标准库与外部组件也减少逻辑复杂性数据访问层模型化:使用SQLAlchemy组件,对数据访问进行对象化建模,屏蔽数据访问细节,如SQL,数据模型易于维护

    2、架构说明新闻网络爬虫程序主题为一系列python脚本,通过文件夹进行模块隔离,基于命名约定进行动态逻辑加载,根目录为ArticleSpider。
    整体框架如下:


    SpiderService.py:服务入口模块,用以处理Windows服务Article Spider Service的安装、卸载、启动、停止与重启
    SpiderTask.py:任务管理模块,负责加载控制规则配置、安排爬虫任务计划、组合爬虫任务子逻辑
    ArticleStorer.py:文章转存模块,包含数据库访问、图片转存与切图、队列消息发送功能
    RuleReader.py:规则读取模块,用于读取爬虫规则,辅助IOC机制
    Spider:爬虫逻辑模块,核心模块群,可根据需要添加新爬虫模板,爬虫模板可继承,基模块为Spider.py,多个相似爬虫可根据规则设置复用同一个爬虫模板
    Model:数据模型模块,维护爬虫相关ORM数据模型,由上下文管理层、数据模型层与事务逻辑层组成
    Message:消息处理模块,主要负责封装与发送队列消息
    SpiderRule.xml:爬虫规则配置,XML格式元数据
    Temp:缓存目录,用以缓存转存完成前的中间文件,如下载图片
    Log:日志目录,用以保存日志,采用循环日志模式
    ServiceCommand.txt:服务入口命令,用于参考的爬虫服务基本人机交互命令
    SpiderTest.py:爬虫测试模块,用于测试的相关脚本

    2.1 模块说明2.1.1 服务入口层 SpiderService.py2.1.1.1 SpiderService
    win32serviceutil.ServiceFramework:服务基类,引入自pywin32组件,用以将Python程序封装位Windows服务
    SvcDoRun:服务启动入口,重载基类方法,用以开启SpiderTask任务管理线程,阻塞等待任务结束事件
    SvcStop:服务终止入口,重载基类方法,用以终止SpiderTask任务管理线程,发起任务结束事件

    2.1.1.2 ServiceCommand
    python SpiderService.py install:爬虫服务安装,必须指令,用以注册Windows服务,安装成功后可直接于Windows服务管理器中进行服务启动、停止等管理操作
    python SpiderService.py install —startup auto:自启动爬虫服务安装
    python SpiderService.py start:启动爬虫服务,服务安装后有效
    python SpiderService.py restart:重启爬虫服务,服务启动后有效
    python SpiderService.py stop:停止爬虫服务,服务启动后有效
    python SpiderService.py remove:删除/卸载爬虫服务,服务安装后有效

    2.1.2 任务管理层 SpiderTask.py2.1.2.1 SpiderTask
    threading.Thread:线程管理基类,引入自python标准库,用以将主任务进行线程化封装
    __init__:初始化方法,进行一系列任务初始化操作,如线程初始化、日志初始化、调度初始化、存储初始化等操作
    ScheduleTask:任务调度方法,读取爬虫规则,根据设置生成爬虫调度计划
    RunSpiderByRule:爬虫驱动方法,按照给定规则,驱动对应爬虫开启任务,核心步骤为,爬虫选用—文章抓取—图片转存—文章入库—后续处理(如压图与消息通知)
    run:任务子线程启动入口,重载基类方法,以日为周期进行调度-执行-休眠循环,苏醒后调度爬虫任务—按照调度计划处理任务(执行或等待)—计划完成后休眠至下一周期苏醒
    StopTask:任务终止方法,当前任务完成后终止任务计划,非强行中断任务子线程,若要强行中断,可直接结束主线程,任务子线程将自动中断

    2.1.3 规则读取层 RuleReader.py2.1.3.1 RuleReader
    __init__:初始化方法,确定规则读取模式(目前仅支持XML模式),模式有效时进行初始规则读取
    FreshRules:规则刷新方法,读取最新规则,默认以XML格式读取规则,若要采用其他方法(如数据库读取、Json接口读取等),可继承该基类后进行重载扩展

    2.1.3.2 GetRuleFromNode功能性函数,从XML节点中提取爬虫规则字典,属性及简单子节点直接提取入本级字典,复杂子节点递归提取为子级字典后加入本级字典。
    2.1.3.3 PrintNode调试用函数,用于打印XML节点数据,采用前序遍历法。
    2.1.3.4 SpiderRule爬虫规则字典(dict类型),存储爬虫规则参数,以福州旅游网爬虫规则为例:

    name:规则名称,通常以爬取来源站命名,如福州旅游网
    sourceId: 来源标识,参照文章来源枚举值,默认为0
    rule:子级明细规则字典
    url:来源Url,明细规则,如:http://lyj.fuzhou.gov.cn/lyjzwgk/lydt/index.htm
    reAbstract:文章摘要正则表达式,明细规则,扫描文章清单时使用,如:
    <li>.+?<span>\[(.+?)\].+?href="(.+?)".+?>(.+?)</a>.+?</li>
    reArticle:文章正文正则表达式,明细规则,扫描文章正文时使用,如:
    <div class="content-boxtext">(.+?)</div>\s*<div class="content-boxbottom">
    reImage:文章图片正则表达式,明细规则,扫描文章图片时使用,如:
    <IMG.+?src="(.+?)".+?/>
    module:爬虫模块名,明细规则,反射加载的爬虫模块名与类名,如FuZhouSpider
    regionId:目的地标识,明细规则,爬虫目的地对应的乐途目的地字典标识值,如130
    spiderName:爬虫编辑ID,明细规则,利用爬虫对内发布文章的虚拟编辑用户名,如jishutest
    isValid:有效标志,明细规则,启用该规则时为1,停用为0,禁用为-1
    minPage:最小页码,明细规则,分页爬取时第一页的页码参数,默认为-1(不分页)
    maxPage:最大页码,明细规则,分页爬取时最后页的页码参数,默认为-1(不分页)
    wakeTime:子级苏醒时间字典,明细规则,可包含多个时间点
    timePotX:时间点,X代表时间点标识,多个时间点标识不可相同,时、分、秒必须

    2.1.4 文章转存层 ArticleStorer.py2.1.4.1 ArticleStorer文章转存器,组织下层通信、数据模块进行数据交互,如数据库访问、队列消息发送、文件上传、远程接口调用等。

    __init__:初始化方法,设定图片转存API(imageApi)、数据库连接(dbConStr)、消息队列(msmqPath)及压图API(picCutApi)等通信参数,并进行初始化
    DumpImage:图片转存方法,转存文章正文中已缓存的的下载图片至图片转存API,同时关联转存图片路径
    DumpArticle:文章入库方法,将已经完成图片转存并替换Url的文章正文按照正文页入库,同时记录爬取关联信息
    NewArticleCheck:新文章检查方法,比对爬取关联信息,确定是否为新文章或可更新文章
    SendSuccessMessage:后期处理消息信息发送方法,向消息队列发送信息,通知后续处理程序处理刚刚发布或变更的新正文页
    CutImaages:正文页压图方法,调用压图接口对指定正文页正文内的图片进行压图处理
    ArticleRepublish:正文页重发布方法,将正文页重新发布,即向消息队连续发送一条删除发布请求与新发布请求

    2.1.5 文章爬虫层 Spider该层为目录,包含多个XSpider.py模块,其中Spider.py为基础模块,其他模块如FuZhouSpider.py为基于Spider.py模块的扩展的模板化模块。
    2.1.5.1 Spider爬虫基类,封装爬虫所需的基本操作,可由子类继承复用(如规则加载、HTML下载、图片下载等共同操作)或重载(文章抓取、图片替换等区别操作)。

    ReadRule:规则加载方法,可继承复用,可嵌套重载,加载爬虫规则字典内参数信息,并对爬虫伪装(Http Header)、数据缓存、数据清理(Css清理、Herf清理等)等参数进行设置
    CatchArticles:文章抓取方法,不可直接复用,必须替换重载,为各爬虫模板的独有逻辑,目前主要有页面抓取及异步JS抓取两种基本模式
    DownLoadHtml:Html源码下载方法,可继承复用,可重载(如进行拟人化访问伪装) ,用于获取抓取页面的相关Html源码,如文章列表页、文章正文页以及异步加载API
    DownLoadImage:图片下载方法,可继承复用,可重载(如应对图片防盗链机制),用于下载文章正文中的图片链接至缓存路径,同时进行图片格式标准化
    ReplaceArticleImages:缓存文章正文图片替换方法,可继承复用,可重载(如对转存图片标签添加属性),用于将抓取文章正文中原来的图片链接替换为转存后的图片链接
    CacheArticle:文章信息缓存方法,可继承复用,可重载(如定制文章特有属性信息),用于组装文章属性信息并加入缓存等待下一步处理
    ClearArticles:文章正文清洗方法,可继承复用,可重载(如添加JS清晰),用于清洗文章正文中的无效信息,如当前已支持的CSS、Herf、空白字符及样式清洗
    ClearTempImages:缓存图片清理方法,可继承复用,可重载(如添加缓存备份机制),用于清理缓存的已下载图片

    2.1.5.2 Functions文章爬虫层中的相关功能性函数。

    ReplaceImage:图像Url替换函数,可将文章正文中的正则匹配项(原始图片链接)替换为转存图片链接
    ClearExternalCss:CSS清理函数,可将文章正文中的正则匹配项(Css类)清空
    ClearExternalHerf:Herf清理函数,可将文章正文中的正则匹配项(超链接)去链接处理
    ClearExternalBlank:空白字符清理函数,可将文章正文中的正则匹配项(空白字符)去除
    ClearExternalStyle:样式清理函数,可将文章正文中的正则匹配项(style样式)去除
    ComposeUrl:相对Url组装函数,将页面中的相对Url结合页面Url组装为绝对Url
    ConvertImageToJpg:图片格式标准化函数,将不同格式的图片(如PNG、BMP)同意转化为JPG格式

    2.1.5.3 XSpider各种爬虫模板类,通常直接继承自Spider,但也可继承其他XSpider,区别主要在于CatchArticles方法的重载实现,目前主要分为两种模式。

    页面爬虫:CatchArticles方法直接解析页面源码,根据制定的编码格式,提取文章关键信息,由扫描列表页的文章摘要、扫描正文页的正文、扫描正文域的图片三步组成,典型模板如FuZhouSpider。
    Json Api爬虫:CatchArticles方法获取Json API响应,将结果反序列化为字典对象,提取文章关键信息,由提取列表API的文章摘要、提取正文API的正文与图片两步组成。,典型模板如FuZhouTourSpider。

    其他类型的模板可根据实际情况自行扩展,如XML API、SOAP API等。
    2.1.6 数据模型层 Model该层为目录,包含多个SpiderContext.py、SpiderEntity.py与SpiderData.py三个模块。

    SpiderContext.py:数据上下文模块,负责数据库连接、数据模型初始化、会话管理
    SpiderEntity.py:数据模型模块,负责数据实体对象模型映射,即表与类的映射
    SpiderData.py:数据逻辑模块,负责组织会话内的数据访问逻辑,也是对外接口

    2.1.6.1 SpiderDataHelper爬虫数据访问类,属于数据逻辑模块。

    SaveArticle:文章入库方法,将爬去并完成过滤的文章信息写入数据库,若是新文章,文章信息入库同时写入文章导入信息,若不是新文章(导入信息已存在),仅进行修改
    NewArticleCheck:新文章检查方法,用于防止文章重复导入,对比文章导入信息,若不匹配(根据文章Url与发布日期),则认定为新文章,否则认定为重复文章
    GetArticlesLikeDampSquibs:未成功发布文章扫面方法,用于查询出已发布但未成功的文章,返回必要属性信息,如正文页ID,目的地ID等

    2.1.6.2 ModelMapper实体关系映射(类表映射)函数,属于数据模型模块,将指定的实体类与数据表绑定,当前已映射对如下:

    Cms_Information—Cms_Information:正文页信息类与正文页信息表
    Cms_Information_Inported—Cms_Information_Inported:正文页导入信息类与正文页导入信息表
    Cms_InformationRegion—Cms_InformationRegion:正文页目的地关联类与正文页目的地关联表

    2.1.6.3 ModelContext数据模型上下文类,属于数据上下文模块,管理数据连接上下文。

    __init__:上下文初始化方法,注册数据库连接、初始化数据模型元数据并建立会话生成器
    Session:会话入口,申请一个数据库会话

    2.1.6.4 Session_Scope数据会话域函数,属于数据上下文模块,负责隔离会话事务的生命周期,具有contextmanager特性,即以迭代生成器的方式隔离会话事务逻辑无关的细节,确保成功自动提交、失败自动回滚并且结束自动释放。
    2.1.7 消息模型层 Message该层为目录,包含SpiderMessageQueue.py模块,负责格式化消息,并与消息队列通信。
    2.1.7.1 Message消息类,负责消息格式化,将消息转化为制定输出格式。

    __init__:初始化方法,将消息字典初始化为XML结构,同时添加必要属性
    ToFormatString:序列化方法,将XML结构的消息转化为字符串,同时添加必要说明性内容

    2.1.7.2 ToXmlElement功能性函数,将字典转化为XML结点格式,简单键值对直接转化为子节点,复杂键值对进行嵌套转化,值数组转化为多个子节点,值为字典则进行递归转化。
    2.1.7.3 MessageQueue消息队列访问类,封装消息队列接口。

    __init__:初始化方法,组装MSMQ队列基本信息,包含主机名与队列路径
    SendMessage:消息发送方法,根据给定MSMQ队列基本信息,创建会话并发送消息

    2.1.7.4 Queue_Scope队列会话域函数,负责隔离队列会话的生命周期,具有contextmanager特性,即以迭代生成器的方式隔离队列会话逻辑无关的细节,确保会话结束自动释放。
    3、部署说明3.1 运行环境
    PC配置

    2G以上内存
    操作系统

    Windows XP Professional 2002 Service Pack 3+
    相关工具

    Python 3.3 Pyodbc 3.0.7 SQLAlchemy 0.9.7pywin32 219requests 2.4.1Miscrosoft Message QueueMiscrosoft SQL Server 2005SQL Server Native Client 10.0

    3.2 资源目录
    源码路径,192.168.80.157主机 E:\shuaxin\ArticleSpider。
    工具路径:192.168.80.157主机 E:\tools\爬虫项目部署包

    3.3 部署步骤3.3.1 Python确认部署主机python-3.x版本已安装,建议使用python-3.3稳定版本。
    若未安装,执行爬虫项目部署包内python-3.3.5.1395976247.msi文件,以安装python虚拟机,安装目录可设置为E:\tools\Python33。
    确保系统环境路径(高级系统设置-环境变量)含有python安装目录,确保路径内可引用python.exe。
    3.3.2 MSMQ确保Miscrosoft Message Queue已开启,且存在消息队列路径\private$\queuepathnews,且该队列对EveryOne开放消息发送权限,若该队列不存在,则依赖部署条件不满足(后续处理程序未部署),不可进行应用部署。
    3.3.3 DataSource Driver确保主机ODBC数据源中已安装SQL Server Native Client 10.0驱动程序(版本可以更高,但ArticleStorer.py中dbConStr也应对应修改)已正确安装。
    若未安装,根据系统环境,选择执行爬虫项目部署包内sqlncli_X86.msi或sqlncli_X64.msi,以安装数据源驱动。
    3.3.4 Pyodbc确保python安装目录(如E:\tools\Python33)下,存在以下相对路径的目录Lib\site-packages\pyodbc…,即Pyodbc已安装。
    若未安装,根据系统环境,执行爬虫项目部署包内pyodbc-3.0.7.win32-py3.3.exe,以安装Python ODBC组件。
    3.3.5 Pywin32确保python安装目录(如E:\tools\Python33)下,存在以下相对路径的目录Lib\site-packages\pythonwin,即Pywin32已安装。
    若未安装,根据系统环境,执行爬虫项目部署包内pywin32-219.win32-py3.3.exe,以安装Python Win32组件。
    3.3.6 Pillow确保python安装目录(如E:\tools\Python33)下,存在以下相对路径的目录Lib\site-packages\PIL,即Pillow已安装。
    若未安装,根据系统环境,执行爬虫项目部署包内Pillow-2.6.0.win32-py3.3.exe,以安装Python Image Library组件。
    3.3.7 Requests确保python安装目录(如E:\tools\Python33)下,存在以下相对路径的目录Lib\site-packages\requests,即Requests已安装。
    若未安装,启动cmd,切换至爬虫项目部署包下requests-2.4.1目录,执行命令python setup.py install,以安装Python Requests组件。
    3.3.8 SQLAlchemy确保python安装目录(如E:\tools\Python33)下,存在以下相对路径的目录Lib\site-packages\sqlalchemy,即SQLAlchemy已安装。
    若未安装,启动cmd,切换至爬虫项目部署包下SQLAlchemy-0.9.7目录,执行命令python setup.py install,以安装Python SQLAlchemy组件。
    3.3.9 SQL Server确保81主机上的lotour库,已存在Cms_Information_Inported表。
    若不存在,执行初始化脚本:
    CREATE TABLE [dbo].[Cms_Information_Inported] ( [SourceUrl] VARCHAR (256) NOT NULL, [Status] SMALLINT NOT NULL, [InformationId] INT NOT NULL, [SourceTime] DATETIME NOT NULL, [RegionId] INT NOT NULL, [SourceName] VARCHAR(50) NULL, PRIMARY KEY CLUSTERED ([SourceUrl] ASC));CREATE NONCLUSTERED INDEX [IX_Cms_Information_Inported_InformationId]ON [dbo].[Cms_Information_Inported]([InformationId] ASC);
    3.3.10 Spider Service检查服务管理程序中,是否存在Article Spider Service服务,即爬虫服务是否已安装。若未安装,选定部署路径,将ArticleSpider目录移动至对应路径,启动cmd,切换至部署目录,执行命令python SpiderService.py install,以安装爬虫服务。
    应用扩展升级时无须重新安装,除非变更SpiderTask.py,亦无须停止Article Spider Service服务,直接添加或替换对应源文件即可(次日生效),但重启服务可确保变更立即生效。
    进入服务管理程序,启动Article Spider Service,若启动成功,整个部署流程完成。
    3.4 配置参数3.4.1 图片转存APIArticleStorer.py中的imageApi参数,指向CMS正文页的图片上传接口,默认为’http:// localhost:8037/WS/newsUploadImage.ashx’。
    3.4.2 数据库连接ArticleStorer.py中的dbConStr参数,对应Lotour库的数据连接字符串,默认为:
    mssql+pyodbc:// testUser:test@localhost:1433/news?driver=SQL Server Native Client 10.0其中mssql表示DBMS为Microsoft Sql Server,pyodbc表示驱动模式为Python ODBC,testUser为数据库用户名,test为用户密码,@至?间区域表示数据库路径,?后区域表示数据库驱动名称。
    3.4.3 消息队列ArticleStorer.py中的msmqPath参数,指向CMS正文页的发布消息队列,默认为:\PRIVATE$\queuepathnews。
    3.4.4 压图APIArticleStorer.py中的picCutApi参数,指向CMS正文页的压图接口,默认为: http://cms.lotour.com:8343/WS/newsCutImage.ashx 。
    3.4.5 图片缓存路径Spider.py中的temp参数,指向文章爬取过程中下载中间图片的缓存目录,默认为相对路径’\temp\‘。
    3.4.6 日志设置SpiderTask.py中的logging.basicConfig函数的相关入口参数,作为爬虫日志设置,可参考python官方文档中的logging部分,当前使用循环日志,部分参数如下:

    filename,日志文件名,默认使用相对路径’\Log\spider.log’
    mode,文件使用模式,默认使用添加模式,即’a’
    maxBytes,循环日志块大小,默认为2M
    backupCount,循环日志块数量,默认为8
    encoding,日志编码,默认为’utf-8’
    format,日志信息格式,用于将Trace信息与Message信息格式化,默认为:
    %(asctime)s %(levelname)-10s[%(filename)s:%(lineno)d(%(funcName)s)] %(message)slevel,日志记录级别,默认为DEBUG,线上部署推荐使用WARN或ERROR

    3.4.7 爬虫规则路径SpiderTask.py中的rulePath参数,默认为XML文件路径,默认值为相对路径’\SpiderRule.xml’。
    4、扩展说明4.1 扩展范围网络爬虫服务扩展分为三个级别,分别为规则扩展、模板扩展以及功能扩展,以应对不同的扩展需求。

    规则扩展:改动规则文件,即SpiderRule.xml,用于爬虫规则微调,以及添加仅需复用爬虫模板的新爬虫(如同站新频道,或同网站架构)
    模板扩展:新增爬虫实现,即添加XSpider.py,继承Spider基类,重载实现特有逻辑(如文章抓取逻辑),必要时可单独添加独立功能,如防盗链破解功能
    功能扩展:变更或重组爬虫功能,任何文件都可能改动,请在理解架构的前提下进行,如将规则元数据由XML元数据变更为可维护更大规模元数据的数据库元数据表,或者将爬虫服务重组以添加文章智能过滤层

    4.2 扩展示例4.2.1 规则扩展规则扩展仅需修改SpiderRule.xml文件,以福州旅游网爬虫为例:
    <rule name="福州旅游网" sourceId="0"><url>http://lyj.fuzhou.gov.cn/lyjzwgk/lydt/index.htm</url><reAbstract><li>.+?<span>\[(.+?)\].+?href="(.+?)".+?>(.+?)</a>.+?</li></reAbstract> <reArticle><div class="content-boxtext">(.+?)</div>\s*<div class="content-boxbottom"></reArticle> <reImage><IMG.+?src="(.+?)".+?/></reImage> <module>FuZhouSpider</module> <regionId>130</regionId> <spiderName></spiderName> <isValid>1</isValid> <minPage>-1</minPage> <maxPage>-1</maxPage> <wakeTime> <timePot0>08:00:00</timePot0> <timePot1>13:00:00</timePot1> </wakeTime></rule>
    一个Rule节点便是一项爬虫规则,结点属性及子节点含义参照3.1.3.4。
    每增加一个爬虫任务,则需要添加对应的Rule节点,添加流程如下:
    4.2.1.1 确定文章来源
    name:根据抓取需求,将name设置为[站名][-频道名]
    sourceId:进入CMS媒体管理模块,检查媒体来源,若不存在现有对应媒体来源,进行添加操作,将sourceId设置为对应媒体来源ID,若不指定媒体来源,忽略sourceId属性或置0
    url:观察抓取url对应源代码,若页面含有文章列表,即可按照网页抓取模式处理,url可直接设置为页面url;若列表内容为异步加载(可参考福州旅游资讯网),此时应分析源代码,找到对应的文章列表API,将url设置为对应API;若url中含有动态参数或分页参数,可使用{n}参数顺序替代(从{0}开始),在抓取逻辑中填入参数
    minPage & maxPage:若需求抓取页面不只一页,则将minPage置为起始页,maxPage置为终止页,同时定制扩展功能,利用参数化的url在抓取逻辑中进行处理
    regionId:抓取源应指定目的地,通常以乐途目的地字典中的ID值配置regionId即可,若有特殊情况(如混合目的地抓取),应在抓取逻辑中提取指定

    4.2.1.2 确定正则表达式
    reAbstract:若文章摘要为页面抓取模式,应分析页面源码,抽象出提取文章摘要信息的正则表达式,其中文章链接必须提取,文章标题与发布时间则是能提取尽量提取,将正则表达式进行html转义处理后置入reAbstract(参考在线转义工具如http://www.cnblogs.com/relucent/p/3314831.html )。若文章摘要为异步加载模式,reAbstract可按照抓取逻辑定制填写,正则表达式不必须,可空置或置为其他数据,如参数化文章正文API
    reArticle:若文章正文为页面抓取模式,应分析页面源码,抽象出提取文章正文信息的正则表达式,其中文章正文必须提取,文章标题与发布时间若在摘要中未提取也必须提取(无法提取时,应在抓取逻辑中另行处理)。若文章正文为异步加载模式,reArticle可按照抓取逻辑定制填写,正则表达式不必须,可空置或置为其他数据,如参数化图片API
    reImage:若图片隐藏在文章正文中,应分析正文代码,抽象出提取图片的正则表达式,图片链接必须提取。若图片信息独立于正文,reImage可按照抓取逻辑定制填写,正则表达式不必须,可空置或置为其他数据,如防盗链破解参数。

    4.2.1.3 确定爬虫模板module:不同规则的爬虫抓取逻辑可能各不相同,也可能存在复用,因此可在规则中指定爬虫模板,若无可复用模板,也可创建新爬虫模板,然后将module置为模板名称;模板名称格式通常为XSpider,不同模板名称不可重复,模板定义可参考模板扩展示例。
    4.2.1.4 确定任务计划
    isValid:爬虫规则可自由预设,但只有将isValid被置为1的规则,在爬虫任务计划扫描时进入任务队列,isValid置为0时表示停用,置为-1时表示禁用,其他取值可在功能扩展中确定
    spiderName:爬虫抓取文章后会发布正文页,默认会以匿名的乐途小编身份发表,此时spiderName置空,若要指定发布人,应将spiderName置为特定编辑者的CMS用户名
    wakeTime:爬虫任务若要执行,至少还需要一个执行时间,爬虫将在指定时间之后被唤醒,执行任务,此时应在wakeTime中添加timePotX子节点,X用于区别多个不同时间点(如0、1、2),意味着统一爬虫可在一日之内的不同时间启动多次

    4.2.2 模板扩展模板扩展需添加XSpider.py文件,在其中实现继承自Spider或其他模板Spider的XSpider类,其中CatchArticles必须重载以实现定制化的抓取逻辑,以福州福州旅游网爬虫为例:
    class FuZhouSpider(Spider.Spider): """福州旅游网 Spider""" def __init__(self): Spider.Spider.__init__(self) def CatchArticles(self): recAbstract = re.compile(self.reAbstract, re.DOTALL) recArticle = re.compile(self.reArticle, re.DOTALL) recImage = re.compile(self.reImage, re.DOTALL) html = self.DownLoadHtml(self.url, '文章列表页{0}访问失败,异常信息为:{1}') if html == None: return self.articles for x in recAbstract.findall(html): article = dict( time = datetime.datetime.strptime(x[0],'%Y-%m-%d'), # url = self.url[0:self.url.rfind('/')] + x[1][1:], url = Spider.ComposeUrl(self.url, x[1]), title = x[2] ) html = self.DownLoadHtml(article['url'], '文章页{0}访问失败,异常信息为:{1}') if html == None: continue content = None images = [] imageCount = 0 for y in recArticle.findall(html): content = y for z in recImage.findall(content): imageCount += 1 # imageUrl = article['url'][0:article['url'].rfind('/')] + z[1:] imageUrl = Spider.ComposeUrl(article['url'], z) image = self.DownLoadImage(imageUrl, '图片{0}提取失败,异常信息为:{1}') if image == None: continue images.append(image) if not content \ or imageCount != len(images): continue self.CacheArticle(article, content, images, '成功自{0}提取文章') return self.articles
    由于采用继承机制,__init__方法中应调用父级__init__方法,CatchArticles方法则可利用基类模板中的方法、字段及函数,结合自有方法、字段及函数扩展抓取逻辑,扩展流程如下:
    4.2.2.1 处理继承要素选定基类
    通常使用Spider.Spider,其中前一个Spider为模块名,对应Spider.py,后一个Spider为类名,如需更细粒度的模板复用,如定制页面抓取模板或异步抓取模板,可继承对应二级模板。
    选定重载方法
    继承模板通常会复用基类模板的大部分方法,但作为区别,必定有自身的特定逻辑,比如重载CatchArticles方法用以实现不同爬虫抓取逻辑(该方法在Spider中为虚方法,继承Spider后必须实现);
    除CatchArticles方法外,其他基类方法也存在扩展空间,可参考3.1.5.1中的方法说明进行重载。
    添加自有元素
    通常情况下,基类元素已经足够使用,但在一些特殊场景,可能需要添加一些新元素,比如文章过滤函数,图片抗反盗链下载之类的功能型函数,然后在重载的方法中使用。
    4.2.2.2 组织抓取逻辑抓取源加载
    根据爬虫规则中定义的url或者Api,从抓取源下载数据;
    页面抓取时,需指定指定抓取页面的编码格式,默认为utf-8编码,但部分页面会使用gbk或者gb2312编码;
    异步抓取或多页抓取时,API通常需要一些需要二次指定的参数,此时应将参数赋值再发起请求;
    抓取源通依照抓取模式存在区别,页面抓取源通常由三段式的文章列表页、文章正文页与文章正文组成,异步抓取源则随API的定义不同而不同,混合抓取则结合了前两者。
    基类抓取相关方法包括DownLoadHtml与DownLoadImage,DownLoadHtml用于加载Html源码或者Api响应数据,DownLoadImage则用于下载图片等文件至缓存路径。
    数据解析与提取
    抓取数据最终需转化为文章基本信息,用于后期处理,其中必要组成部分包含文章摘要信息(文章源链接、标题及发布日期等)、文章正文以及图片清单(图片链接及缓存路径等);
    页面抓取数据需要使用正则表达式解析,对应正则表达式由基类从爬虫规则读取,由reAbstract(文章摘要规则)、reArticle(文章正文规则)以及reImage(图片规则)组成,分别可提取信息至article(文章基本信息)、content(文章正文)以及images(图片列表);
    异步抓取通常不需要使用正则表达式,因为抓取数据通常为结构化数据,可以直接反序列化为友好的数据结构,如Json数据,可先进行清洗(将null替换为Python中的None),然后直接使用eval函数转化为数据字典;
    reAbstract、reArticle及reImage在异步抓取或混合抓取中的存储涵义开放,可配合模板自行定义,如API,筛选规则等,只需要保证最终可正确提取文章基本信息;
    提取文章发布时间时,应注意时间数据的表示形式,指定转化格式(如%Y-%m-%d可匹配2014-10-1),并注意从摘要数据和从正文数据提取的区别;
    正则解析函数主要使用findall进行全文搜索,注意该函数匹配结果为多项时将返回匹配结果迭代器,为单项时将直接返回匹配结果;
    信息提取完成后,应调用基类的CacheArticle方法,将article、content与images组装并缓存,等待后起批量处理。
    4.2.2.3 文章后期处理通常情况下图像后期处理对模板扩展透明,但其中的部分环节可进行独立定制,文章后期处理的流程为:
    有效性检查—图片转存—文章正文图片链接替换—正文清洗—文章入库—文章图片压图—发布。
    其中模板扩展可参与环节主要为文章正文图片链接替换(ReplaceArticleImages)与正文清洗(ClearArticles)环节。
    ReplaceArticleImages扩展示例如更改img标签的替换形式,如加入alt等属性。
    ClearArticles扩展示例如增加过滤规则,如JS过滤。
    4.2.3 功能扩展功能扩展自由度较大,但通常情况下是对爬虫服务任务流程进行重组。
    爬虫服务主流程为:
    扫描爬虫任务—执行爬虫任务—休眠,其功能扩展主要集中在任务管理层与规则读取层。
    特定规则的执行流程为:
    加载任务—文章抓取—有效性检查—图片转存—文章正文图片链接替换—正文清洗—文章入库—文章图片压图—发布,其功能扩展主要集中在文章转存层、数据模型层以及消息模型层。
    功能扩展的方式主要有两种,加入中间层以添加中间步骤以及重载替换特定步骤,对应的可扩展空间请参阅架构说明部分。
    1 留言 2019-05-02 11:18:20 奖励25点积分
  • 编程使用WMI 精华

    背景
    WMI出现至今已经二十多年了,但很多人对它并不熟悉。知道它很好很强大,但不知道它从哪里来,怎么工作,使用范围是什么?
      WMI有一组API。我们不管使用VBScript、PowerShell脚本还是利用C#的来访问WMI的类库,都是因为WMI向外暴露的一组API。这些API是在系统安装WMI模块的时候安装的,通过他们我们能够能拿到我们想要的类。
      WMI有一个存储库。尽管WMI的多数实例数据都不存储在WMI中,但是WMI确实有一个存储库,用来存放提供程序提供的类信息,或者称为类的蓝图或者Schema。
      WMI有一个Service。WMI总是能够响应用户的访问,那是因为它有一个一直运行的Windows服务,名字叫Winmgmt。停止这个服务,所有对WMI的操作都将没有反应。
      WMI是可扩展的。人人都知道WMI能干很多事情,读取本机硬盘信息、读取远程计算机的用户信息、读取域用户信息等等。基本上,你能想到的获取或者更改资源的操作,它都能干。可谓吃得少,干得多。它为什么这么能干呢?这基于WMI的可扩展性。WMI对资源的操作,不是它自己实现了什么方法,而完全取决于向它注册的提供程序。
      WMI是管理员日常必备的强大工具之一,是脚本伴侣。当然也可以把一个大型系统建立在WMI以及WMI的提供程序之上。
    WMI的全称是Windows Management Instrumentation,即Windows管理工具。它是Windows操作系统中管理数据和操作的基础模块。我们可以通过WMI脚本或者应用程序去管理本地或者远程计算机上的资源。对于VC和汇编程序员,想获取诸如CPU序列号和硬盘序列号等信息是非常容易的。但是对于VB以及其他一些脚本语言,想尝试获取系统中一些硬件信息可能就没那么容易了。微软为了能达到一种通用性目的(遵守某些行业标准),设计了WMI。它提供了一个通过操作系统、网络和企业环境去管理本地或远程计算机的统一接口集。应用程序和脚本语言使用这套接口集去完成任务,而不是直接通过Windows API。可能有人要问,为什么不让设计的脚本直接在底层使用Windows API,而非要弄个新的技术呢?原因是在目前Windows API中,有些是不支持远程调用或者脚本调用的。这样通过统一模型的WMI,像VB和脚本语言就可以去访问部分系统信息了。但是并不是所有脚本语言都可以使用WMI技术:它要支持ActiveX技术。

    WMI通常是被脚本所调用的,不过也对。对于WMI能做的操作,我们也完全可以通过VS去调用Win32 API去实现。但是,本文要讲的是使用VS调用WMI提供的接口去获取系统的信息。那么,VS中怎么使用WMI呢?接下来,我就把我所了解的知识分享给大家。
    函数介绍CoInitializeEx 函数
    为当前线程初始化COM库并设置并发模式 。
    函数声明
    HRESULT CoInitializeEx( void * pvReserved, DWORD dwCoInit);
    参数

    pvReserved系统 保留的参数,必须传入 NULL。dwCoInit该标示指明基于当前线程的并发模式和初始化选项。该参数是 COINIT 枚举类型,传入参数时候,除了COINIT_APARTMENTTHREADED 和COINIT_MULTITHREAD ED标记外,其余的标记可以组合使用。
    返回值

    S_OK :COM库初始化成功。S_FALSE :当前线程上,COM库已经被初始化。RPC_E_CHANGED_MODE :COM库已经被初始化且传入参数设置的并发模式和本次不同。

    CoInitializeSecurity 函数
    注册安全性并设置进程的默认安全性值。
    函数声明
    HRESULT CoInitializeSecurity( _In_opt_ PSECURITY_DESCRIPTOR pSecDesc, _In_ LONG cAuthSvc, _In_opt_ SOLE_AUTHENTICATION_SERVICE *asAuthSvc, _In_opt_ void *pReserved1, _In_ DWORD dwAuthnLevel, _In_ DWORD dwImpLevel, _In_opt_ void *pAuthList, _In_ DWORD dwCapabilities, _In_opt_ void *pReserved3);
    参数

    pSecDesc [in]服务器将用于接收呼叫的访问权限。cAuthSvc [in]asAuthSvc参数中的条目计数。只有当服务器调用CoInitializeSecurity时,此参数才被COM使用。如果此参数为0,则不会注册认证服务,并且服务器无法接收安全呼叫。值为-1表示COM选择要注册的身份验证服务,如果是这种情况,则asAuthSvc参数必须为NULL。但是,如果参数为-1,则服务器将不会选择Schannel作为身份验证服务。asAuthSvc [in]一组服务器愿意用来接收呼叫的认证服务。pReserved1 [in]此参数是保留的,必须为NULL。dwAuthnLevel [in]进程的默认身份验证级别。dwImpLevel [in]代理的默认模拟级别。此参数的值仅在进程为客户端时使用。pAuthList [in]指向SOLE_AUTHENTICATION_LIST的指针,它是一个SOLE_AUTHENTICATION_INFO结构的数组。dwCapabilities [in]通过设置一个或多个EOLE_AUTHENTICATION_CAPABILITIES值指定的客户端或服务器的附加功能。pReserved3 [in]此参数是保留的,必须为NULL。
    返回值

    S_OK :COM库初始化成功。RPC_E_TOO_LATE:CoInitializeSecurity 已经被调用。E_OUT_OF_MEMORY:内存不足。

    CoCreateInstance 函数
    用指定的类标识符创建一个COM对象,用指定的类标识符创建一个未初始化的对象。
    函数声明
    STDAPI CoCreateInstance( REFCLSID rclsid, //创建的Com对象的类标识符(CLSID) LPUNKNOWN pUnkOuter, //指向接口IUnknown的指针 DWORD dwClsContext, //运行可执行代码的上下文 REFIID riid, //创建的Com对象的接口标识符 LPVOID * ppv //用来接收指向Com对象接口地址的指针变量);
    参数

    rclsid[in] 用来唯一标识一个对象的CLSID(128位),需要用它来创建指定对象。pUnkOuter[in] 如果为NULL, 表明此对象不是聚合式对象一部分。如果不是NULL, 则指针指向一个聚合式对象的IUnknown接口。dwClsContext[in] 组件类别. 可使用CLSCTX枚举器中预定义的值。riid[in] 引用接口标识符,用来与对象通信。ppv[out] 用来接收指向接口地址的指针变量。如果函数调用成功,*ppv包括请求的接口指针。
    返回值

    S_OK:指定的Com对象实例被成功创建。REGDB_E_CLASSNOTREG:指定的类没有在注册表中注册. 也可能是指定的dwClsContext没有注册或注册表中的服务器类型损坏。CLASS_E_NOAGGREGATION:这个类不能创建为聚合型。E_NOINTERFACE:指定的类没有实现请求的接口, 或者是IUnknown接口没有暴露请求的接口。

    CoSetProxyBlanket 函数
    设置将用于在指定代理上进行呼叫的认证信息。
    函数声明
    HRESULT CoSetProxyBlanket( _In_ IUnknown *pProxy, _In_ DWORD dwAuthnSvc, _In_ DWORD dwAuthzSvc, _In_opt_ OLECHAR *pServerPrincName, _In_ DWORD dwAuthnLevel, _In_ DWORD dwImpLevel, _In_opt_ RPC_AUTH_IDENTITY_HANDLE pAuthInfo, _In_ DWORD dwCapabilities);
    参数

    pProxy [in]要设置的代理。dwAuthnSvc [in]要使用的身份验证服务。dwAuthzSvc [in]要使用的授权服务。pServerPrincName [in]要与身份验证服务一起使用的服务器主体名称。dwAuthnLevel [in]要使用的认证级别。dwImpLevel [in]要使用的模拟级别。pAuthInfo [in]指向建立客户端身份的RPC_AUTH_IDENTITY_HANDLE值的指针。由句柄引用的结构的格式取决于认证服务的提供者。dwCapabilities [in]这个代理的功能。
    返回值

    S_OK:执行成功。E_INVALIDARG:一个或者多个参数无效。

    实现原理现在,我们来解析下在VS2013中使用WMI的原理过程:

    首先,我们要使用CoInitializeEx初始化COM组件环境。因为WMI是基于COM组件实现的,使用COM组件前,必须要进行初始化操作。
    然后,使用CoInitializeSecurity注册安全性并设置进程的默认安全性值。
    接着,使用CoCreateInstance创建一个IWbemLocator的COM对象。
    跟着,通过IWbemLocator::ConnectServer函数连接到WMI,并获取IWbemServices指针。
    然后,使用CoSetProxyBlanket设置连接的安全级别。
    接着,使用IWbemServices指针发出WMI请求,执行WQL语句。
    然后,从查询返回集中获取获取返回数据。
    进行清理工作。

    其中,WQL其实非常简单,它有如下特点:
    SELECT 属性名 FROM 类名每个WQL语句必须以SELECT开始,SELECT后跟你需要查询的属性名,也可以像SQL一样,以*表示返回所有属性值。然后,FROM关键字,即你要查询的类的名字。
    本文使用的WQL语句是:SELECT * FROM Win32_Process。
    编码实现加载WMI所需的库文件#include <comdef.h>#include <WbemIdl.h>#pragma comment(lib, "wbemuuid.lib")
    调用WIM获取进程信息int WMI_EnumProcess(){ /*******************************************/ // 初始化操作 // /*******************************************/ // 初始化COM组件 HRESULT hRes = ::CoInitializeEx(NULL, COINIT_MULTITHREADED); if (FAILED(hRes)) { printf("CoInitializeEx Error[%d]\n", hRes); return 1; } // 设置进程的安全级别 hRes = ::CoInitializeSecurity(NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_DEFAULT, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE, NULL); if (FAILED(hRes)) { printf("CoInitializeSecurity Error[%d]\n", hRes); return 2; } /*******************************************/ // 创建一个WMI命名空间连接 // /*******************************************/ // 创建一个CLSID_WbemLocation对象 IWbemLocator *pIWbemLocator = NULL; hRes = ::CoCreateInstance(CLSID_WbemLocator, NULL, CLSCTX_INPROC_SERVER, IID_IWbemLocator, (LPVOID *)(&pIWbemLocator)); if (FAILED(hRes)) { printf("CoCreateInstance Error[%d]\n", hRes); return 3; } // 使用 pIWbemLocator 连接到 "root\cimv2", 并获取 pIWbemServices IWbemServices *pIWbemServices = NULL; hRes = pIWbemLocator->ConnectServer(_bstr_t(L"ROOT\\CIMV2"), NULL, NULL, NULL, 0, NULL, NULL, &pIWbemServices); if (FAILED(hRes)) { printf("pIWbemLocator->ConnectServer Error[%d]\n", hRes); return 4; } /*******************************************/ // 设置连接的安全级别 // /*******************************************/ // 设置连接的安全级别 hRes = ::CoSetProxyBlanket(pIWbemServices, RPC_C_AUTHN_WINNT, RPC_C_AUTHN_NONE, NULL, RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE); if (FAILED(hRes)) { printf("CoSetProxyBlanket Error[%d]\n", hRes); return 5; } /*******************************************/ // 执行WQL查询代码 // /*******************************************/ // 这里是列出正在运行进程的例子 // 为了接收结果, 你必须定义一个枚举对象 IEnumWbemClassObject *pIEnumWbemClassObject = NULL; hRes = pIWbemServices->ExecQuery(bstr_t("WQL"), bstr_t("SELECT * FROM Win32_Process"), // 类似数据库中的 SQL 语句 WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, NULL, &pIEnumWbemClassObject); if (FAILED(hRes)) { printf("pIWbemServices->ExecQuery Error[%d]\n", hRes); return 6; } // 显示查询结果 IWbemClassObject *pIWbemClassObject = NULL; ULONG ulRet = 0; VARIANT varProp = { 0 }; while (pIEnumWbemClassObject) { // 获取下一个对象 hRes = pIEnumWbemClassObject->Next(WBEM_INFINITE, 1, &pIWbemClassObject, &ulRet); // 判断获取完毕结束 if (0 == ulRet) { break; } // 获取 ProcessId 字段的值 并 显示 ::VariantClear(&varProp); pIWbemClassObject->Get(L"ProcessId", 0, &varProp, NULL, NULL); printf("[%d]\t", varProp.intVal); // 获取 Name 字段的值 并 显示 ::VariantClear(&varProp); pIWbemClassObject->Get(L"Name", 0, &varProp, NULL, NULL); printf("[%ws]\n", varProp.bstrVal); } /*******************************************/ // 清理释放 // /*******************************************/ // 释放 ::VariantClear(&varProp); pIWbemServices->Release(); pIWbemLocator->Release(); ::CoUninitialize(); return 0;}
    程序测试在 main 函数中调用上述封装好的函数进行测试,main 函数为:
    int _tmain(int argc, _TCHAR* argv[]){ // 调用 WMI 去查询进程信息 WMI_EnumProcess(); system("pause"); return 0;}
    测试结果:
    运行程序,进程信息成功显示。所以测试成功。

    总结在VS中使用WMI,流程总是这么几步。要查询其他信息,也只是更改下WQL查询语句以及查询结果的显示而已。如果你学过数据库,或者了解过数据库相关的知识的话,你会发现WQL查询语句和数据SQL语句是很相似的。那是因为,我们之前提到的,WMI也有自己的数据库,也会在数据库里存储一些信息。
    参考文档WMI入门(一):什么是WMI
    WMI技术介绍和应用——WMI概述
    《Windows黑客编程技术详解》一书
    1 留言 2018-11-30 21:25:55 奖励30点积分
  • 大数据 13、推荐算法-CF

    前文链接:https://write-bug.com/article/2376.html
    推荐算法在大数据Hadoop平台2-2、MapReduce和上节的实践中都提到了检索或推荐系统的数据来源的底层来源,那么这里即是召回阶段的算法。算法目的是侧重召回候选集合,粗排。
    我们在看爱奇艺网站时,上面会有一些推荐的热榜栏目和个性化推荐栏目等,那么这里的基础算法是什么样的呢?
    推荐算法简单分为两种:基于内容和协同过滤(行为)推荐
    1.1 基于内容Content base1.1.1 引入Item属性的Content Based推荐
    在前面介绍过的实践中,我们利用token实现item的倒排,其实就类似找到了一些item和item之间的相关性信息。我们做的正排表的分词就是此图内容分析索引,而相关性计算就是倒排索引,把它保存在nosql后就可以直接查询了。

    通常在网站主页中,不会做大面积的个性化推荐,其透漏着隐私问题会给用户带来困扰。
    1.1.2 引入User属性的Content Based推荐

    这里我们需要把item的token改成可以描述我用户行为偏好的token。
    个性化:

    Step1:user搜索、点击、收藏过这些item时(也就是用户行为数据),可以用这些item的token 给 (用户分析) user做用户画像(兴趣建模)
    Step2:—->倒排索引—-》token—-》索引—->item
    Step3:相关计算cos、jaccard ∩/∪等
    Step4:排序

    1.2 基于协同Collaboration Filtering核心:User对Item的打分形成UI稀疏矩阵(即只存储有行为的数据)。
    存储可以类比原来的倒排索引:userid(key)—->(value)item item item
    原来的倒排:一个token被几个item同时包含。
    这里的倒排:一个用户对几个item有过行为,即被一个用户作用过的几个item有着相关性。

    那稀疏矩阵是什么样子呢?

    数据例:
    1,100001,5 1,100002,3 1,100003,4 1,100004,3 1,100005,3 1,100007,4 1,100008,1 1,100009,5 1,1000011,2这个稀疏矩阵就是UI矩阵,只存储用户有行为的数据,由此数据量可以大大减小:即倒排索引。
    随着时间的推移,日志服务器会积累越来越多的行为数据形成UI矩阵。通常中型以上的互联网公司,用户量最少一亿以上(一个用户多个账号),音乐量级几十万以上,所以这个UI矩阵很大很大,需要用稀疏矩阵存储。

    由这个UI矩阵我们知道,userA对ItemBCD有过行为,那么由前面倒排索引的知识,我们可以得到itemA被哪些用户有过行为,得到了userlist,那我把ItemB为key就得到了UserA的好友列表。例:微博
    根据user A的好友列表userBCD,可以得到每个user背后的item列表,把这些item聚合推荐给userA。例:个性化推荐
    根据itemA背后的user列表做相似度关联,把每个用户背后的item聚合,在和itemA相关联。例:非个性化推荐


    可由1我们得到user和user的矩阵即UU=UI*IU(UI的转置矩阵);可由3我们得到item和item的矩阵即II=IU*UI。由此我们引出了两种协同:User-Base CF和Item-Base CF。
    1.2.1 user-Base —- CF:—————外交假设:
    – 用户喜欢那些跟他有相似爱好的用户喜欢的东西
    – 具有相似兴趣的用户在未来也具有相似兴趣
    方法:
    – 给定用户u,找到一个用户的集合N(u),他们和u具有相似的兴趣
    – 将N(u)喜欢的物品推荐给用户.

    UI矩阵计算——————》 UU矩阵—————-用户之间相似度
    流程:转置,归一,cos
    目的:如果把一个user没看过的电影推荐给他,打一个分预测用户,对这个电影有多大的兴趣

    r(userid,itemid)=两个用户的相似度×另一个用户的打分 / 所有用户(随机选择一部分)相似度加和这个结果,只需要基于同一套算法(UU / II)的排序,不需要具体分数。
    1.2.2 item -Base —- CF:———自身历史行为假设:
    – 用户喜欢跟他过去喜欢的物品相似的物品
    – 历史上相似的物品在未来也相似
    方法:
    – 给定用户u,找到他过去喜欢的物品的集合R(u)
    – 把和R(u)相似的物品推荐给u
    UI矩阵计算——————》 II矩阵—————电影之间相似度
    流程:转置,归一,cos
    目的:如果把一个user没看过的电影推荐给他,打一个分预测用户,对这个电影有多大的兴趣

    r(userid,itemid)=两个item的相似度×该用户的打分 / 所有item(随机选择一部分)相似度加和什么时候用哪个算法?

    性能角度:谁维度小用谁
    领域:UB时效性(看是否火,用户之间相互影响),IB倾向于个性化(历史相似物品)
    实时性:IB更高:新行为导致推荐结果实时变化
    冷启动:UB需要时间离线计算,IB围绕产生新行为的物品无限推荐
    推荐理由:UB难解释

    冷启动三类:—————-新东西——粗粒度吸引

    用户:推荐热门排行榜、注册年龄性别等做画像粗粒度推荐、社交网站导入好友信息推荐、新登陆反馈标签推荐
    物品:新物品通过基于内容推荐——-CB
    系统:不太好解释的,用户画像(职业)和物品(不懂这东西)———引入专家知识(知识图谱,人工校验等)建立相关物品矩阵

    I I/UU 相似度计算公式:———————cos

    类比cos=向量内积 / 向量模 ------把两个劈开相乘
    ——————> 归一化 ui / ui^2———————->两个Rui 评分相乘
    比如上面的两个电影 M 和 T 的相似度是0.57
    U(i,j)=A B D
    R(ua,M)=5 , R(ua,T)=1R(ub,M)=1 , R(ub,T)=5R(ud,M)=4 , R(ud,T)=3归一化:
    25+1+16=42---根号42M:5/1/4/1+25+9=35------根号35T:1/5/3/M*T=实践:协同过滤————————给定一个item,推荐相关的item集合(II矩阵)
    倒排式、分块式
    倒排式流程:
    遍历排列组合同一个人观看的所有item,得到很多相似度的pair;如果两个物品被越多的人贡献过,相似度越准确,去除偶然性误点。也就是排列出所有组合pair对,-----有几个相同对,相当于被几个人贡献过。相同的pairkey会分在同一个桶里,计算相似度分数就会越准确---套公式(reduce中)

    Step1:归一UI矩阵:

    Map:
    矩阵输入数据:U,I,S输出:Key(i),Value(u,s)对
    Reduce:
    输入:Key(i),Value(u,s)对输出:Key(u),Value(i,s_new)对

    Step2:衍生 I I Pair对:

    Map:
    输入:Key(u),Value(i,s_new)对输出:Key(u),Value(i,s_new)对
    Reduce:
    输入:Key(u),Value list((i,s_new))对输出:Key_1(i) , Value_1(j,s_new_is_new_j)Key_2(i) , Value_2(i,s_new_is_new_j)

    Step3:result :

    Map:
    输入: Key(i) , Value(j,s_new_i*s_new_j)输出:Key<i,j> , Value(s_new_i*s_new_j)
    Reduce:
    输入:Key<i,j> , Value list((s_new_i*s_new_j))输出:Key(i) , Value(j,score)


    前面说了一些最基础的推荐算法,但是其中存在了一些问题,我们会在后面更新基础的聚类算法中指出。并且在其中我们使用倒排式实现了协同过滤,突出了MR批量计算和对大规模UI矩阵的批量处理,但在其中代码的缺点也很明显,我们输入的UI矩阵产生pair对时,会产生比UI大得多的数据,并会有一些重复数据,很容易爆掉内存,所以我们需要优化在UI矩阵中取物品需要对每一个用户的物品候选集合设置阈值,也就是pair对多少的阈值,如果说一个用户点击了几乎所有item,pair数据指数级增长,内存就会立刻爆掉。后面:聚类、MF(ALS、LFM、SVD、SVD++)、ANN
    2 留言 2019-04-25 11:23:42 奖励20点积分
  • 【Cocos Creator实战教程(6)】——镜头跟随

    1. 知识点讲解Camera就是标记一块区域,这块区域就是镜头范围。
    拿拍电影来举例子,当拍一个场景时,场景里常常有主角,而主角常常有主角光环,所以摄像机的镜头就要对准主角,镜头要跟着主角移动。
    这里的镜头移动方式有两种。一种是中心移动,也就是以主角为中心,主角移动镜头就同步移动;另一种是边界移动,这种移动方式下我们给镜头设定一个范围,当主角移动出这个范围时,摄像机才移动。
    我们这里用的是边界移动的方式(中心移动的我也试了一下,真的有点晕)。
    2. 步骤新建一个Camera节点并且添加脚本,改变位置。
    CameraManager2.js
    cc.Class({ extends: cc.Component, properties: { bgSky:cc.Node, skySca:0, bgHill:cc.Node, hillSca:0, bgHillnear:cc.Node, hillnearSca:0, bgFloor:cc.Node, floorSca:0, cloudLayer:cc.Node, cloudSca:0, camera: cc.Node, player: cc.Node, }, init: function(game){ this.game = game; }, moveBg: function(distance){ //当主角跳出镜头边界时镜头才移动 if(this.player.y > this.camera.y+this.camera.height/2){ this.bgSky.y -= distance * this.skySca; this.bgHill.y -= distance * this.hillSca; this.bgHillnear.y -= distance * this.hillnearSca; this.bgFloor.y -= distance * this.floorSca; this.cloudLayer.y -= distance * this.cloudSca; this.player.y -= distance; } }});
    3. 总结如果大家想对两种视角有个更好的了解,可以试试英雄联盟的视角锁定,自由视角和中心视角的切换按钮是Y。
    这几次的知识点并成一个游戏给大家。
    2 留言 2018-11-27 01:36:29 奖励20点积分
  • ring0层下实现的文件强制删除

    7.6 文件强删在日常生活使用电脑的过程中,遇到删不掉的文件的时候,我们总会通过一些软件提供的强制删除文件功能来删除顽固的文件。文件强删技术对于杀软来说是清楚病毒木马的武器,在扫描器检测出恶意文件的时候,就需要强删功能来清除恶意文件。而对于病毒木马来说,反过来可以用强删技术来强制删除系统保护文件或是受杀软保护的文件。
    本文将会介绍文件强删技术,即使文件正在运行,也能强制删除本地文件。
    实现过程当文件是PE文件而且已经被加载到内存中的时候,正常情况下是无法通过资源管理器explorer.exe来删除本地文件的,因为在删除运行中的文件或者被加载的DLL文件的时候,系统会调用MmFlushImageSection内核函数来检测文件是否处于运行状态,若是,则拒绝删除操作。其中,系统中的MmFlushImageSection内核函数,主要是通过检查文件对象中的PSECTION_OBJECT_POINTERS结构数据来判断该文件是否处于运行状态、是否可以删除。
    同时,在发送IRP删除文件的时候,系统同样会判断文件的属性是否是只读,若是只读属性则会拒绝删除操作。
    所以,根据上述的删除原理,文件强制删除的具体实现流程如下所示。
    首先,发送IRP打开删除文件,并获取文件对象。
    然后,发送IRP设置文件属性,属性类型为FileBasicInformation,将文件属性重新设置为FILE_ATTRIBUTE_NORMAL,防止原来的文件属性为只读属性。并保存文件对象中的PSECTION_OBJECT_POINTERS结构的值,保存完成后,再对该结构进行清空处理。
    接着,发送IRP设置文件属性,属性类型为FileDispositionInformation,实现删除文件操作。这样,即使是运行中的文件也能被强制删除。
    最后,还原文件对象中的PSECTION_OBJECT_POINTERS结构,并调用ObDereferenceObject函数释放文件对象,完成清理工作。
    那么,实现文件强制删除的实现代码如下所示。
    // 强制删除文件NTSTATUS ForceDeleteFile(UNICODE_STRING ustrFileName){ NTSTATUS status = STATUS_SUCCESS; PFILE_OBJECT pFileObject = NULL; IO_STATUS_BLOCK iosb = { 0 }; FILE_BASIC_INFORMATION fileBaseInfo = { 0 }; FILE_DISPOSITION_INFORMATION fileDispositionInfo = { 0 }; PVOID pImageSectionObject = NULL; PVOID pDataSectionObject = NULL; PVOID pSharedCacheMap = NULL; // 发送IRP打开文件 status = IrpCreateFile(&pFileObject, GENERIC_READ | GENERIC_WRITE, &ustrFileName, &iosb, NULL, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0); if (!NT_SUCCESS(status)) { DbgPrint("IrpCreateFile Error[0x%X]\n", status); return FALSE; } // 发送IRP设置文件属性, 去掉只读属性, 修改为 FILE_ATTRIBUTE_NORMAL RtlZeroMemory(&fileBaseInfo, sizeof(fileBaseInfo)); fileBaseInfo.FileAttributes = FILE_ATTRIBUTE_NORMAL; status = IrpSetInformationFile(pFileObject, &iosb, &fileBaseInfo, sizeof(fileBaseInfo), FileBasicInformation); if (!NT_SUCCESS(status)) { DbgPrint("IrpSetInformationFile[SetInformation] Error[0x%X]\n", status); return status; } // 清空PSECTION_OBJECT_POINTERS结构 if (pFileObject->SectionObjectPointer) { // 保存旧值 pImageSectionObject = pFileObject->SectionObjectPointer->ImageSectionObject; pDataSectionObject = pFileObject->SectionObjectPointer->DataSectionObject; pSharedCacheMap = pFileObject->SectionObjectPointer->SharedCacheMap; // 置为空 pFileObject->SectionObjectPointer->ImageSectionObject = NULL; pFileObject->SectionObjectPointer->DataSectionObject = NULL; pFileObject->SectionObjectPointer->SharedCacheMap = NULL; } // 发送IRP设置文件属性, 设置删除文件操作 RtlZeroMemory(&fileDispositionInfo, sizeof(fileDispositionInfo)); fileDispositionInfo.DeleteFile = TRUE; status = IrpSetInformationFile(pFileObject, &iosb, &fileDispositionInfo, sizeof(fileDispositionInfo), FileDispositionInformation); if (!NT_SUCCESS(status)) { DbgPrint("IrpSetInformationFile[DeleteFile] Error[0x%X]\n", status); return status; } //还原旧值 if (pFileObject->SectionObjectPointer) { pFileObject->SectionObjectPointer->ImageSectionObject = pImageSectionObject; pFileObject->SectionObjectPointer->DataSectionObject = pDataSectionObject; pFileObject->SectionObjectPointer->SharedCacheMap = pSharedCacheMap; } // 关闭文件对象 ObDereferenceObject(pFileObject); return status;}
    测试在64位Windows 10操作系统上,运行C:\520.exe程序后,直接加载并运行上述驱动程序,强制删除正在运行的520.exe文件。520.exe本地文件成功被删除,如图7-6所示。

    小结本文介绍的文件强删技术的实现原理是,通过发送IRP打开文件,获取文件对象;发送IRP设置文件属性,去掉只读属性;清空文件对象中的PSECTION_OBJECT_POINTERS结构,使运行中的程序文件能够被删除;最后发送IRP删除文件并释放文件对象。
    本文介绍的文件强删技术可以删除正在运行的exe文件,但是不适用于前面介绍的发送IRP打开文件不关闭的文件保护方法。要想强制删除发送IRP打开文件不关闭的文件保护,需要关闭打开的文件对象。可以利用XCB解锁技术,通过硬编码定位出文件对象中SCB(Stream Control Block)、FCB(File Control Blokc)、CCB(Context Control Block)结构的CleanupCount变量,并将CleanupCount都置为1,再调用ObDereferenceObject函数关闭文件对象并释放资源。XCB解锁技术不仅可以解锁发送IRP打开文件的保护方式,同样适用于硬链接文件保护。
    参考参考自《Windows黑客编程技术详解》一书
    1 留言 2019-04-28 10:04:32
  • python数据分析(4)——数据预处理(上)

    数据预处理的主要内容包括数据清洗、数据集成、数据变换和数据规约。

    1 数据清洗数据清洗主要是删除原始数据集中的无关数据、重复数据,平滑噪声数据,筛选掉与挖掘主题无关的数据,处理缺失值、异常值。
    1.1 缺失值处理方法主要分为删除记录、数据插补和不处理,其中常用的数据插补方法如下。

    这里主要介绍拉格朗日插值法和牛顿插值法。其他的插值方法还有Hermite插值、分段插值和样条插值。


    P(x)是牛顿插值逼近函数,R(x)是误差函数。
    3)将缺失的函数值对应的点x代入插值多项式得到缺失值的近似值f(x).
    牛顿插值法也是多项式插值,但采用了另一种构造插值多项式的方法,与拉格朗日插值相比,具有承袭型和易于变动节点的特点。从本质上来说,两者给出的结果是一样的(相同次数、相同系数的多项式),只不过表示的形式不同。因此,在Python的Scipy库中,只提供了拉格朗日插值法的函数(因为实现上比较容易),如果需要牛顿插值法,则需要自行编写函数。
    例子是catering_sale.xls
    #拉格朗日插值代码import pandas as pd #导入数据分析库Pandasfrom scipy.interpolate import lagrange #导入拉格朗日插值函数inputfile = 'catering_sale.xls' #销量数据路径outputfile = 'sales.xls' #输出数据路径data = pd.read_excel(inputfile) #读入数据data[u'销量'][(data[u'销量'] < 400) | (data[u'销量'] > 5000)] = None #过滤异常值,将其变为空值#自定义列向量插值函数#s为列向量,n为被插值的位置,k为取前后的数据个数,默认为5def ployinterp_column(s, n, k=5): y = s[list(range(n-k, n)) + list(range(n+1, n+1+k))] #取数 y = y[y.notnull()] #剔除空值 return lagrange(y.index, list(y))(n) #插值并返回插值结果#逐个元素判断是否需要插值for i in data.columns: for j in range(len(data)): if (data[i].isnull())[j]: #如果为空即插值。 data[i][j] = ployinterp_column(data[i], j)data.to_excel(outputfile) #输出结果,写入文件
    1.2 异常值处理
    我们一般将异常值视为缺失值进行插补。
    python中判断每个元素是否空值/非空值

    D.isnull/notnull()

    2. 数据集成2.1 实体识别实体识别是指从不同数据源识别出现实世界的实体,它的任务是统一不同源数据的矛盾之处。
    2.1.1 同名异义数据源中同样是属性ID,不一定是同一实体。
    2.1.2 异名同义不同数据源中不同名字的数据项表示是同一实体。
    2.1.3 单位不统一描述同一个实体分别用的是国际单位和中国传统的计量单位。
    检测和解决这些冲突就是实体识别的任务。
    2.2 冗余属性识别
    同一属性多次出现同一属性命名不一致导致重复
    有些冗余属性可以用相关分析检测。
    python去除数据中的重复元素

    D.unique()
    np.unique(D)
    1 留言 2018-12-25 14:24:18 奖励15点积分
  • 5G将渗透哪些领域

    5G爆发前夕将渗透哪些领域?5G 已经被吹捧为许多细分市场的新的赋能者,它将给移动电话、自动驾驶、虚拟现实(VR)和物联网等行业带来机会。但是,这个新的无线通信标准将在何时和将以何种方式影响这些细分市场,以及它将对半导体设计产生什么样的影响,目前尚有很多问题和不确定性。
    基于 5G 承诺的通信速度的大幅度提高和延迟的大幅度降低,系统供应商必须在将数据处理放在本地或在云端之间作出决定。这将对半导体体系结构产生重大影响,包括处理器和内存的片上吞吐量、I/O 速度、功率预算,甚至电池大小等等。此外,这些决定还将受到 5G 基础设施接入和通信频率的影响。
    不过,这些事情可能要等到数年后才会发生。一开始,大部分的 5G 应用将在低于 6GHz 的范围内,也就是和 4.5G 相当。最大的受益者将是移动电话行业,它将在一段时间内仍然是 5G 技术的最大消费者。移动电话的标准和存储容量将会因为 5G 技术的不断引入而持续进化,它们将依然是 5G 技术的发展的重要资金来源。
    下一个阶段的发展是在毫米波技术引进之时。这时候最重要的变化将开始发生。一般的经验法则是,任何一项新技术想要成功,必须提供10倍的增益,要么是性能提高、或功率降低、成本降低、面积更小或者多种增益的组合。只有在那个时候,5G 技术才会真正地大放光彩。
    “5G 将在连接性能方面提供显著的改进,其目标是比目前 4G 的连接性能提高1000倍”。Steven Woo 说,他是 Rambus 实验室负责系统和解决方案的副总裁、杰出的发明家。 “除了带宽方面的改进,5G 还承诺了降低延迟和更好的覆盖范围。”
    在毫米波技术的应用初期,设计成本和硅晶元出货面积将会显著上升。电力消耗将成为主要的问题,这取决于基础设施和它能承受的负荷压力,因为它们需要承担信号发送和信号到达时的计算工作。
    物联网一个真正能从 5G 中受益的领域是边缘计算,其中功耗是一个限制因素。“我们希望 5G 的引入能使物联网边缘设备的功耗更低,因为平均来说 5G 拉近了它们与接入点的距离。” Cadence 的产品营销总监 Neil Robinson 说:“这意味着与 4G 通信所需要的更长距离相比,5G 通信所需要的功耗更低”。
    这样就打开了一扇大门,让我们有能力处理比目前更加复杂的处理流程和通信方案。Woo 说:“5G 将提供更高的带宽,这意味着越来越多的终端(endpoint)将会更容把它们的数据传输到相邻位置,在那里这些数据得以在本地处理。”这意味着只有少量的,级别更高的数据/信息,才需要传输到云端处理。
    但这种方案也会很快变得复杂。“5G 所需的高带宽和多天线策略意味着,从本质上讲,它们需要更高的能耗。”西门子仿真部门市场营销高级总监 Jean-Marie Brunet 指出。“然而,人们普遍认为物联网的大部分将是机对机(M2M)通信。而机对机通信模式比人工启动的物联网更容易预测,因此机对机实例的低功耗算法应该更高效,这种说法是有争议的。”
    实际上的好处将随着不同的应用和区域有所不同。“归根结底,5G 将使每比特功耗降低10倍到100倍,同时带来相当于10倍的带宽增幅,这些变化将在元器件的寿命方面产生净增益,并同时提高10倍的功效。因使用模式的不同这些好处会有所不同。”
    这是否足以影响架构?物联网边缘设备已开始将推理运算本地化,以避免与向云端传输大量原始数据而带来的带宽影响。
    “更高的带宽和更低的延迟将使在云端进行推理运算变得更容易,如果我们需要这样做的话。” Cadence 的 Robinson 说。“但是,隐私、安全、延迟和功耗问题可能会让这种方式变得不合适。”
    Cadence 的 Tensilica IP 产品管理和营销高级总监 Lazaar Louis 也回应了这些担忧。“对隐私和安全问题的担忧将会使推理运算继续留在边缘设备进行,”他说。“将传感器收集到的信息传送到云端会消耗能量,因此在边缘设备进行推理运算,可以节省在边缘设备上消耗的能量。”
    Rambus 实验室的 Woo 同意这一说法。“更高的带宽可能不能排除在边缘设备上进行推理运算的必要性,但它们将允许更大数量的物联网设备相互连接,使基础设施能够跟上不断增长的物联网设备的需求,并使其捕获和传输的数据量不断增长。”
    VR(虚拟现实)和AR(增强现实)虚拟现实(VR)碰到了难题。如果没有更高的数据速率,设备供应商将很难消除晕眩不适感,这大大地限制了 VR 的使用。毫米波技术可以帮助解决这个问题。虽然毫米波信号不能通过墙壁,只能在相对较短的距离内工作,但 VR 耳机和控制器的距离通常只有几英尺远。
    “与以前运行同一款游戏的 4G 产品相比,高分辨率的 8K 游戏流媒体肯定会更快地将电池的电量耗光。” Brunet 承认 “但是这并不是 5G 造成的,而是因为更高级的 CPU 和显示设备。在毫米波频率下,对于空中下载(OTA)的功耗需求,收发器的功耗将会以加速度级地增加。”
    自动驾驶自动驾驶需要将许多技术结合在一起,5G 就是其中之一。“更低的延迟为具有自主驾驶能力的联网车辆带来了好处,在这种情况下,响应时间至关重要,尤其是在高速公路的行驶速度下。”Woo说道。“5G 覆盖范围的改进,加上增强的本地处理能力,将允许在数据产生的终端设备附近对数据进行聚合和处理,从而减少数据传输。减少长距离的数据传输对于 5G 来说是一个重要的好处,因为它可以同时改进了延迟和功耗。”
    汽车很可能成为通讯枢纽。“汽车将是微型发射器,” Robinson 说。“和现在的 802.11p(V2V)和4G/OnStar(V2X)面临的情况类似,5G 也面临着来自电视网络的阻挠,他们希望使用已经分配给 V2V(车辆对车辆)的相同频段。他们声称,自动驾驶不需要 V2V,在路上使用 V2X(车辆对所有)就可以来了解其他车辆的存在/意图。”
    这个观点得到其他人的同意。“车辆到所有(V2X)将主导通信,车辆将成为拥有很多发射器的移动网络,在许多情况下,每个功能域都有多个发射器。” Brunet 说:“V2X 将是实现安全的关键因素,因为激光雷达或雷达根本看不到拐角处,而 5G 可以依据拐角处的反射将这种实现变得可能。”
    到底是从其他车辆还是从路边的信息中得到这些信息,现在尚没有明确的答案。“汔车能获得的信息越多,它们就越能在自动驾驶体验方面做出更好的决定。” Cadence 公司的 Louis 指出。 “更先进的自动驾驶将得益于和其他车辆/基础设施的 V2X 通信,例如路线规划和车道变更辅助。”
    新的通信能力也可能带给我们今天还没有想到的好处。“联网车辆只是众多可能受益的设备之一。” Woo 说。“更高的带宽和更高的覆盖率,再加上更强的本地处理能力,将帮助车辆与周围环境以及本地地图数据进行通信,从而在将来实现道路导航。今天的联网汽车已经捕获了大量的数据,其中一些信息被传送到云端。预计汽车将继续发展而成为信息枢纽,因为它们可以作为乘客设备的连接点(就像手机可以作为智能手表和健身设备等外围设备的连接点一样),并与其他车辆进行点对点通信,以交流像车道变化之类的预期行动,以及交通状况和危险信息。”
    实施注意事项今天大多数 5G 的实现都还只是原型,而且并不是所有的问题都已经得到解决。例如,5G 可以提供每秒10到20千兆位的传输速度,但数字系统必须具备相应的工作速度才能受益于 5G 的高速度。如果为了降低成本而仍然使用旧的工艺节点生成数字信息,这可能使得能使用的工艺节点变得有限,或者我们需要更先过的封装解决方案。
    “这是摩尔定律的一个结合,它减慢了速度,同时增加了芯片的复杂性和所需要的工艺的复杂性。。” Rambus 的产品管理高级主管 Frank Ferro 表示:“你不需要做一个大型的 ASIC,你必须问一下分解它是否更划算。做两个更小的 ASIC 或重新使用你已经进行了大量投资的混合信号器件是否更便宜?如果你已经在高速工艺技术上进行了投资,你想继续扩大规模吗?或者,您可以使用现有的技术,加上接口技术,而不必每次更改工艺节点时都开发 SerDes 呢?“
    同样,不同的产品领域可能会得出不同的结论。“我不知道 5G 节点的期望值。” Robinson 说道。“这将归结为每个产品或公司的成本
    1 留言 2019-04-22 10:31:20 奖励15点积分
  • Ring0内核层下实现删除文件或空目录

    背景本文讲解的文件的基础操作就是指对文件进行增、删、改、查等基础的操作,这也是管理文件常用的操作。在应用层,我们都是直接使用 WIN32 API 来直接操作。到了内核层,虽然不能使用 WIN32 API,但是 Windows 也专门提供了相应的内核 API 给我们开发者使用,我们直接调用内核 API 就能实现我们的功能。
    现在,我就单独将文件基础管理的常用操作一一解析,每个技术点都形成独立文档,来向大家讲解下怎么使用内核 API 在内核下创建文件或目录、删除文件或目录、 计算获取文件大小、文件的读和写操作实现文件复制、文件重命名、文件查询遍历等。
    本文主要讲解的是,使用内核 API 删除文件或空目录的实现原理和过程。
    函数介绍InitializeObjectAttributes 函数
    InitializeObjectAttributes宏初始化不透明OBJECT_ATTRIBUTES结构,该结构指定了打开句柄的例程的对象句柄的属性。
    函数声明
    VOID InitializeObjectAttributes( [out] POBJECT_ATTRIBUTES InitializedAttributes, [in] PUNICODE_STRING ObjectName, [in] ULONG Attributes, [in] HANDLE RootDirectory, [in, optional] PSECURITY_DESCRIPTOR SecurityDescriptor);
    参数

    InitializedAttributes [out]指定要初始化的OBJECT_ATTRIBUTES结构。ObjectName [in]指向Unicode字符串的指针,该字符串包含要打开句柄的对象的名称。这必须是完全限定的对象名称,或者是由RootDirectory参数指定的对象目录的相对路径名。Attributes[in]指定一个或多个以下标志:OBJ_INHERIT该句柄可以由当前进程的子进程继承。OBJ_PERMANENT此标志仅适用于在对象管理器中命名的对象。默认情况下,当所有打开的句柄关闭时,这些对象将被删除。如果指定了此标志,则当所有打开的手柄都关闭时,该对象不会被删除。驱动程序可以使用ZwMakeTemporaryObject删除永久对象。OBJ_EXCLUSIVE此对象只能打开单个句柄。OBJ_CASE_INSENSITIVE如果指定了此标志,则在将ObjectName参数与现有对象的名称匹配时使用不区分大小写的比较。否则,使用默认系统设置比较对象名称。OBJ_OPENIF如果将此标志指定给创建对象的例程,并且该对象已经存在,则例程应该打开该对象。否则,创建对象的例程将返回STATUS_OBJECT_NAME_COLLISION的NTSTATUS代码。OBJ_KERNEL_HANDLE指定句柄只能在内核模式下访问。OBJ_FORCE_ACCESS_CHECK打开句柄的例程应强制执行对象的所有访问检查,即使在内核模式下打开句柄。RootDirectory [in]在ObjectName参数中指定的路径名的根对象目录的句柄。如果ObjectName是一个完全限定的对象名称,则RootDirectory为NULL。使用ZwCreateDirectoryObject获取对象目录的句柄。SecurityDescriptor [in]指定在对象创建时应用于安全描述符。此参数是可选的。驱动程序可以指定NULL来接受对象的默认安全性。
    返回值

    无返回值。

    ZwDeleteFile 函数
    删除指定文件。
    函数声明
    NTSTATUS ZwDeleteFile( _In_ POBJECT_ATTRIBUTES ObjectAttributes);
    参数

    ObjectAttributes [in]指向OBJECT_ATTRIBUTES结构的指针,其中包含调用者为文件对象提供的属性。 这些属性将包括ObjectName和SECURITY_DESCRIPTOR。 该参数通过调用InitializeObjectAttributes宏来初始化。
    返回值

    成功时,返回STATUS_SUCCESS;失败时,返回适当的NTSTATUS错误代码。

    实现原理对于文件的删除,在应用层上我们使用 DeleteFile 来实现,删除空目录则使用 RemoveDirectory。而对于内核层来说,我们直接可以使用 ZwDeleteFile 函数来删除指定文件以及空目录。现在,我们给出具体实现步骤:
    首先,我们调用 InitializeObjectAttributes 宏来初始化对象属性,包括文件或者目录路径和其它属性。
    然后,再调用 ZwDeleteFile 函数,根据传入的对象属性参数删除指定对象。如果对象是个文件,则删除文件;若对象是一个空目录,则删除目录。注意,若对象是一个非空目录,则返回不操作。
    特别注意一点,内核下表示的文件或者目录路径要在路径前面加上 \??\,例如表示 C 盘下的 test.txt 文件路径:\??\C:\test.txt。
    编码实现删除文件或空目录// 删除文件或是空目录 \??\C:\MyCreateFolder\520\520.exeBOOLEAN MyDeleteFileOrFileFolder(UNICODE_STRING ustrFileName){ NTSTATUS status = STATUS_SUCCESS; OBJECT_ATTRIBUTES objectAttributes = { 0 }; InitializeObjectAttributes(&objectAttributes, &ustrFileName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL); // 执行删除操作 status = ZwDeleteFile(&objectAttributes); if (!NT_SUCCESS(status)) { ShowError("ZwDeleteFile", status); return FALSE; } return TRUE;}
    程序测试在 Win7 32 位系统下,驱动程序正常执行;在 Win10 64 位系统下,驱动程序正常执行。
    总结ZwDeleteFile 内核函数和应用层的 DeleteFile 函数类似的,都是删除指定文件。不同的是,ZwDeleteFile 还可以删除目录,若对象是一个空目录,则删除目录。注意,若对象是一个非空目录,则返回不操作。
    特别注意一点,内核下表示的文件或者目录路径要在路径前面加上 \??\,例如表示 C 盘下的 test.txt 文件路径:\??\C:\test.txt。
    参考参考自《Windows黑客编程技术详解》一书
    2 留言 2019-01-26 18:53:59 奖励8点积分
  • C语言-测试时用来快速转换数组与单链表

    数组与链表操作结构体定义typedef struct ListNode {int val;struct ListNode *next; };数组转化为单链表void getHeadNode(int a[],int length, ListNode* get) { struct ListNode *q; for (int i = 0; i < length; i++) { struct ListNode* p = (struct ListNode *)malloc(sizeof(struct ListNode)); p->val = a[i]; p->next = NULL; q = get; while (q->next != NULL) { q = q->next; } q->next = p; //如果不需要头结点则反注释 ////释放头指针 //q = get; //get = get->next; //free(q); } }输出单链表//存在头结点void outputHead(struct ListNode* h){ while (h->next!= NULL) { printf("%d ", h->next->val); h = h->next; } printf("\n");}
    1 留言 2018-11-09 20:59:52 奖励5点积分
  • python爬虫--爬取网站中的多个网页

    爬取7k7k小游戏的URL
    # -*- coding: utf-8 -*-"""Created on Sun Mar 24 10:04:58 2019@author: pry"""import requestsfrom bs4 import BeautifulSoupimport osimport reimport urllibfrom lxml import etreedef parse_page(): t = 1 headers = { 'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3642.0 Safari/537.36' } for i in range(1,5): url_i = 'http://www.7k7k.com/flash_fl/461_' + str(i) + '.htm' response_i = requests.get(url_i, headers = headers) selector = etree.HTML(response_i.text, parser=etree.HTMLParser(encoding = 'utf-8')) print(url_i) content = selector.xpath('//a/@href') for i in content: if i[0] == "j": continue if i[0] == "/": i = url_i + i with open('7k7k_urls.txt','a+') as file: file.write(i) file.write("\n") file.close() print(i) t = t + 1 print(t) print('ok')if __name__ == '__main__': parse_page()
    1 留言 2019-04-25 08:45:45 奖励5点积分
  • 大数据 12、中文分词

    前文链接:https://write-bug.com/article/2367.html
    文本相似度一般在讨论相似度时,我们讨论相似度与距离之间的关系:相似度越低,距离越远
    我们这里先分为两类:
    语义相似:

    个人简介
    人物介绍

    解决方案:协同过滤(用户行为)、回归算法
    eg:用户搜索歌神时,大部分人点击张学友,由此两个词间便存在了某种联系
    字面相似:

    我吃饱饭了
    我吃不饱饭

    解决方案:LCS最长公共子序列、中文分词(词:token)
    eg:通过token列出词粒度集合,通过算法打分或者取交集求取相似度
    解决字面相似:
    1.cosine余弦相似度
    以前在中学学习余弦时都是基于二维坐标系:
    cos(θ)=a·b/|a|* |b|我们词粒度的维度是由token数量决定的,由此我们拓展到n维得:

    • 句子A:(1,1,2,1,1,1,0,0,0)
    • 句子B:(1,1,1,0,1,1,1,1,1)

    a,b即通过分词后得到的词粒度集合向量化
    注意:由分词得到的去重集合(BOW bag of word)一旦列举出后顺序不能变
    计算流程:

    通过TFIDF找出两篇文章的关键词
    每篇文章各取出若干个关键词,合并成一个集合,计算每篇文章对于这个集合中的 词的词频
    生成两篇文章各自的词频向量
    计算两个向量的余弦相似度,值越大就表示越相似

    TFIDF:找关键词
    1)TF(Term Frequency):词频
    即假设如果一个词很重要,那么它在这篇文章中会多次出现。但是,出现最多的不一定是最重要的,比如停用词:“的”“是”“在”。。。(可提前过滤)那么再次假设关键词:在当前文章出现较多,但在其他文章出现少。
    公式:
    TF1=某词在文章中出现次数 / 文章词总数(基数大) 越大越重要TF2=某词在文章中出现次数 / 该文章次数最多的词的出现的次数(基数小)TF越大,越重要
    2)IDF:反文档频率,即某一个词在某些文章中出现个数多少的衡量
    公式:
    log(语库文档总数 / 包含该词的文档数+1) > 0单调函数,x越大,y越平滑。出现少,分母越小,IDF越大,词越重要。
    score= TF*IDF像停用词这样的词,TF大,IDF小
    拓展:自动摘要
    1)确定关键词集合
    两种方法:
    1.score—Top10
    2.阈值截断>0.8
    2)那些句子包含关键词,取出这些句子
    3)对关键词排序,对句子做等级划分(含该关键词的分数线性加和)
    4)把等级高的句子取出来,就是摘要
    优化:关键词算法、中心思想、第一末尾自然段等等。
    tfidf:

    优点:简单快速,结果比较符合实际状况
    缺点:缺少通顺度,而且关键词不是唯一的权重,比如开头结尾,中心思想。

    单纯以“词频”做衡量标准,不够全面,有时重要的词可能出现的次 数并不多
    实践:
    1.分词
    2.数据预处理,把所有文章内容全部收集到一个文件中,每行为一篇文章
    convert.py input_tfidf_dir/ >convert.data3.MR批量计算IDF

    map.py: word list去重,每一个单词在一篇文章中出现一次计1
    reduce.py: wordcount + IDF计算

    得到token,score集合
    排序:cat part-00000 |sort -k2 -nr |head
    2.LCS 最长公共子序列:顺序性,不连续
    从字面的角度,衡量字面相似度的方法之一
    应用:

    推荐场景 , item推荐,推荐列表—-保持多样性,满足新鲜感需求,放在推荐引擎中可方便调控相似度阈值过滤(即候选集合后)
    辨别抄袭
    生物学家常利用该算法进行基因序列比对,以推测序列的结构、功能和演化过程

    – 字符串12455与245576的最长公共子序列为2455
    – 字符串acdfg与adfc的最长公共子序列为adf
    计算方法:
    暴力穷举:abc排列组合,取LCS,穷举搜索法时间复杂度O(2^m \∗ 2^n);
    动态规划法:
    抽象公式:

    当xy两字符串最后一位相等时+1,并循环求lcs,不相等时求左右各减一位的最大值,如果有一方为空循环停止,值为0
    代码二维数组:

    i和j为行和列,从上到下从左到右看,最开始都为空,行列都为0,接着从左到右有相同项左上角+1,不相同的左和上取最大值,最后得出最大序列数
    score=4len(x)=7len(y)=6sim(x,y)=4*2/6+7=0.615*2为了归一化实践:
    通过数组维护tokenlist,左右两个字符串按照上面形式计算。
    lcs适合粗粒度过滤,适合场景没有cos广泛因为机器学习中涉及到cos的地方太多了
    3.中文分词中文并不像英文,每个单词自动空格分隔,并且没有一个衡量标准好坏问题,词粒度分割不同,意思也就不同。
    我们前面一直提到搜索和推荐场景,在这里,推荐适合粗粒度的分词,因为需要关心词的语义去推荐,而搜索适合细粒度的分词场景,侧重于召回更多的item到候选集合。
    表示方法:
    那我们如果对于一句话做分词,人类可以一眼就看出来,那么对于计算机来说怎么表示,那我们假设,切开位置表示为1,其他位置为0;那么这句话表示为:有/意见/分歧——》11010本田雅阁/汽车——》100010那么这种表示方法对计算机确实很友好,但是对我们来说很头疼,于是通常情况下,我们用分词节点序列表示:有/意见/分歧——》{0,1,3,5}
    那么接下来的问题,这串索引怎么得到呢?这里有一种原始方法:最大长度查找(字典匹配)这个又分两种:前向查找,后向查找。加载词典:
    在查找过程中我们如果一个词一个词去匹配未免过于耗费资源,所以这里用一个加速查找字典的方法:trie树—数据结构 查找过程如下:• 从根结点开始一次搜索;• 取得要查找关键词的第一个字母,并根据该字母选择对应的子树并转到该子树继续进行检索;• 在相应的子树上,取得要查找关键词的第二个字母,并进一步选择对应的子树进行检索。• 迭代过程……• 在某个结点处,关键词的所有字母已被取出,则读取附在该结点上的信息,即完成查找。其他操作类似处理.时间复杂度 O(n)—-空间换时间数据结构,并且在python中的字典数据结构这种速度是近乎于O(1)
    匹配词典:
    前面这是加载词典,而匹配词典是下面几种方法:1、最大长度查找前向查找:先从头匹配第一个字,之后看第二个字是否在第一个字后面,以此类推,若不在则砍一刀,之后继续查找当前第一个字。后向查找:即语料库词为倒序,即反向建库通常情况下后向查找会更符合语义。2、我们把每个字都分开,之后在语料库中匹配出每种排列组合的可能,用前面说的索引序列表示,与此同时,我们把每种可能都用一条线首尾相连,而这么多条线就构成了一张图:即有向无环图,也叫DAG图。
    由此一句话的编码表示:DAG: {0: [0, 1, 3], 1: [1], 2: [2, 3, 5], 3: [3], 4: [4, 5], 5: [5], 6: [6, 7], 7: [7]}
    那么我们这些边该如何去切分和选择呢?
    由此引出:语言模型概念
    我们由语料库已经有了很多列举后的切分方案,那么我们由这些切分方案可以得到每条方案的条件概率,比如:C=本田雅阁汽车S1=本田/雅阁/汽车S2=本田雅阁/汽车则:P(S1|C)表示S1这条切分方案的条件概率
    目标:我们希望得到的就是最大概率的那条切分方案。
    那我们如何求取这条概率呢?:贝叶斯公式
    p(s|c)=p(c|s)p(s)/p(c)
    推导:p(s|c) p(c)=p(s,c)—联合概率(同时发生的概率)->p(c|s)p(s)=p(c,s)-> p(s|c) p(c)= p(c|s)p(s)-> p(s|c)= p(c|s)p(s)/p(c)
    P(c):即这句话的概率,但是人类说每句话的概率都是相同的,即常量。所以有:p(s|c)= p(c|s)p(s)P(c|s):即p(本田雅阁汽车|本田/雅阁/汽车)=100%,即不管哪种切分方案都可以还原出原始的句子。所以有:p(s|c)= p(s)目标:P(S)独立性假设,每个词之间无任何联系,都是独立的那么有:p(本田/雅阁/汽车)=p(本田)p(雅阁)p(汽车)也就是每个词token的概率:即TF——word count由于P(S1)>p(S2),所以选P(S1)
    那么由此我们选择概率最大的那条路线,就是这条句子的切分最佳方案。
    但是,我们概率相乘是很容易形成很小的小数的,造成向下溢出,所以有:
    求log后因为概率是小于1的数所以为负数,可比性高,并且加法运行速度比乘法更快。
    我们前面是假设每个token前后是无关联关系的,但在实际生活中词与词都是有关联的,所以我们刚才的概率模型就是一元模型(只考虑到了词频概率)。
    多元模型:一元模型(Unigram) :一个词的出现不依赖于它前面出现的词—3个参数P(S)=P(w1) P(w2)P(w3)…P(wn)二元模型(Bigram):一个词的出现仅依赖于它前面出现的一个词—3+6个参数P(S)=P(w1) P(w2|w1)P(w3|w2)…P(wn|wn-1)三元模型 (Trigram):简化成一个词的出现仅依赖于它前面出现的两个词。3+6+。。
    每次参数排列组合爆炸级增长。
    J i e b a 分词简介官方链接:https://github.com/fxsjy/jiebajieba用于中文分词,支持的文本编码格式为utf-8,支持的功能包括:中文分词、关键字提取、词性标注整体功能如下图:
    框架结构:
    第一阶段:加载词库,用trie树分割,生成句子中汉字所有可能成词情况所构成的DAG图—-FULL
    词表格式: token,TF,词性 ——-》trie树——>DAG图——》找概率Route概率:获得词频最大切分P= TF / total(TF总和)—》log(p)—-》负数总词数:Linux:awk汉字计数:cat dict.txt | awk ‘BEGIN{a=0}{a+=$2}END{print a}’—— 60101967(total)
    第二阶段:动态规划或者贝叶斯计算最大路径概率,找出最大切分组合,Token英文中文识别我们前面得到了每条边和每个汉字的概率(即所有可能词的概率),接着用倒序索引找最大路径(类似开始说的第二种表示方法)route: {0: (-33.271126717488308, 1),1: (-32.231489259807965, 1),2: (-23.899234625632083, 5),3: (-31.52324681384394, 3),4: (-22.214895405024865, 5),5: (-19.00846787368323, 5),6: (-8.7800749471799175, 7),7: (-8.800692190498415, 7), 8: (0.0, ‘’)}从后向前导,每种切分方案是单个字概率大还是词概率大,确定后的方案概率不变,继续向前导,求最大方案,并累加概率值,以此类推。所以最后方案:0-1,2-5,6-7
    第三阶段:所有单个汉字的传给——>HMM模型粘贴词库中没有的被切碎的词(未登录词)—->再传回来改回为词更新句子,Viterbi动态规划算法
    HMM:隐马尔科夫模型定位:针对单字的粘合
    马尔可夫模型每个状态只依赖之前有限个状态之前的二元模型可等价于一阶马尔可夫模型——即依赖前一个状态 p(w1,w2,w3,……,wn) = p(w1)p(w2|w1)p(w3|w1,w2)……p(wn|w1,w2,……,wn-1) =p(w1)p(w2|w1)p(w3|w2)……p(wn|wn-1) 例如: p(w1=今天,w2=我,w3=写,w4=了,w5=一个,w6=程序) =p(w1=今天)p(w2=我|w1=今天)p(w3=写|w2=我)……p(w6=程序|w5=一个)马尔可夫模型3类参数: 状态(对于分词来说,每一个token即一个状态) 初始概率(某开头词汇出现的文章次数 / 总文章数),例p(w1=今天) 转移概率(条件概率) 统计:(P(B|A)=2/3 A出现时B出现概率)
    马尔可夫模型缺点:只是对一串序列做建模做不了情况:两个序列:实时翻译、文字和词性、语音识别
    由此引出了HMM—-隐马尔科夫模型——生成式模型即对观察序列(O)和隐藏序列(S)进行分析处理
    状态序列(隐藏序列)由:《位置信息(BMES)+词性》B:begin M:middle E:end S:single——做分词我/今天/真高兴==》SBEBME词性:如果一个词是N词,那么他的字也是N词—做词性标注
    HMM有5类参数:状态序列(隐藏序列)M个*4=256初始概率 M个转移概率 M*M发射概率 M*N观察序列 N个=常用汉字
    jieba表:位置+词性+概率prob_start:初始概率—————-256个prob_trans:转移概率,从一个状态到另一个状态的概率值,词性一样prob_emit:发射概率,从状态到字的概率,每个位置和词性后面匹配的字的概率获取方法:文章统计(词性人工标注)在中文分词中,我们需要用大量的已经分好词的数据作为先验知识,在其中统计BMES找到一些规律prob,以便于我们后期利用工具做中文分词。
    假设有3个观察、3个状态:P(S1,S2,S3,O1,O2,O3)联合概率(同时发生)=P(S1)*P(O1|S1)*P(S2|S1)…. 初始、发射、转移我们的目标:给定HMM模型,我们输入观测序列,希望得到最优的状态序列即最优的分词策略P(S|O)=P(S,O) / P(O) P(O)=100%P(S|O)=P(S,O)所有遍历一遍时间复杂度:M^T
    最优路径:动态规划:Viterbi只需要关心当前状态与前一状态的最优路径(最大概率),两两计算,再次向后迭代score1: t位置的状态概率,score2:从t到t+1的转移概率概率,O:从状态到字的概率计算:score1初始概率*score2转移概率*O发射概率=score3新分数——-类似孩子长个复杂度:M*M*(N-1)
    实践:1.MR批量分词Client本地:代码、jieba包Datanode:数据文件分发—file模式—-代码内解压
    2.pyweb+分词Get、Post引用jieba方法
    3.中文分词完成倒排索引正排索引item-》token——》分词倒排索引token-》item——->召回
    (1)map正排:一对一:name->token—————————分词+jieba权重scoretoken1^Ascore token2^Bscore(2)map倒排:token-》name
    (3)reducetoken1-》name name name———>wordcount思路拼接字符串
    1 留言 2019-04-12 12:23:32 奖励11点积分
  • C++语言算符优先分析器的设计与实现

    一、实验目的及要求1.1 目的加深对语法分析器工作过程的理解;加强对算符优先分析法实现语法分析程序的掌握;能够采用一种编程语言实现简单的语法分析程序;能够使用自己编写的分析程序对简单的程序段进行语法翻译。
    1.2 要求
    对语法规则有明确的定义
    编写的分析程序能够对实验一的结果进行正确的语法分析
    对于遇到的语法错误,能够做出简单的错误处理,给出简单的错误提示,保证顺利完成语法分析过程
    软件、硬件环境:code::blocks 10.05,win10系统

    二、实验步骤2.1 实验步骤
    定义目标语言的语法规则
    求解预测分析方法需要的符号集和分析表
    输入将要进行分析的句子
    根据预测分析的方法进行语法分析,直到源程序结束
    对遇到的语法错误做出错误处理

    2.2 文法定义E->E*TF->TT->(E)T->i2.3 求出FIRSTVT和LASTVTFIRSTVT LASTVTE * ( i * ) iT ( i ) i2.4 画出算符优先分析表* ( ) i #* > < > < >( < < = < >) > > i > > ># < < < =三、实验内容3.1 流程图3.2 源代码#include<stdlib.h>#include<stdio.h>#include<string.h>#include<iostream>#define SIZE 128char priority[6][6]={{0},{'>','>','<','<','>','>'},{'>','>','$','$','>','>'},{'<','<','<','<','=','$'},{'>','>','$','$','>','>'},{'<','<','<','<','$','='}};char input[SIZE]; char remain[SIZE]; char AnalyseStack[SIZE]; int testchar(char x),k; void remainString(); int testchar(char x){ int m; if(x=='+') m=0; if(x=='*') m=1; if(x=='i') m=2; if(x=='(') m=3; if(x==')') m=4; if(x=='#') m=5; return m;}void analyse(){ int i,j,f,z,z1,n,n1,z2,n2; int count=0; char a; char p,Q,p1,p2; f=strlen(input); for(i=0;i<=f;i++) { a=input[i]; if(i==0) remainString(); if(AnalyseStack[k]=='+'||AnalyseStack[k]=='*'||AnalyseStack[k]=='i' ||AnalyseStack[k]=='('||AnalyseStack[k]==')'||AnalyseStack[k]=='#') j=k; else j=k-1; z=testchar(AnalyseStack[j]); if(a=='+'||a=='*'||a=='i'||a=='('||a==')'||a=='#') n=testchar(a); else //如果句子含有不是终结符集合里的其它字符,不合法 printf("错误!该句子不是该文法的合法句子!\n"); break; p=priority[z][n]; if(p=='$'){ printf("错误!该句子不是该文法的合法句子!\n"); return; } if(p=='>'){ for( ; ; ){ Q=AnalyseStack[j]; if(AnalyseStack[j-1]=='+'||AnalyseStack[j-1]=='*'||AnalyseStack[j-1]=='i' ||AnalyseStack[j-1]=='('||AnalyseStack[j-1]==')'||AnalyseStack[j-1]=='#') j=j-1; else j=j-2; z1=testchar(AnalyseStack[j]); n1=testchar(Q); p1=priority[z1][n1]; if(p1=='<') { count++; printf("(%d) %s\t%10c\t%5c%17s\t 归约\n",count,AnalyseStack,p,a,remain); k=j+1; i--; AnalyseStack[k]='N'; int r,r1; r=strlen(AnalyseStack); for(r1=k+1;r1<r;r1++) AnalyseStack[r1]='\0'; break; } else continue; } } else{ if(p=='<'){ count++; printf("(%d) %s\t%10c\t%5c%17s\t 移进\n",count,AnalyseStack,p,a,remain); k=k+1; AnalyseStack[k]=a; remainString(); } else{ if(p=='='){ z2=testchar(AnalyseStack[j]); n2=testchar('#'); p2=priority[z2][n2]; if(p2=='='){ count++; printf("(%d) %s\t%10c\t%5c%17s\t 接受\n",count,AnalyseStack,p,a,remain); printf("该句子是该文法的合法句子。\n"); break; } else{ count++; printf("(%d) %s\t%10c\t%5c%17s\t 移进\n",count,AnalyseStack,p,a,remain); k=k+1; AnalyseStack[k]=a; remainString(); } } else printf("错误!该句子不是该文法的合法句子!\n"); break; } } }}void remainString(){ int i,j; i=strlen(remain); for(j=0;j<i;j++) remain[j]=remain[j+1]; remain[i-1]='\0';}int main(){ printf("请输入要进行分析的句子(以#号结束输入):\n"); gets(input); k=0; printf("步骤 栈 优先关系 当前符号 剩余输入串 移进或归约\n"); AnalyseStack[k]='#'; AnalyseStack[k+1]='\0'; int length,i; length=strlen(input); for(i=0;i<length;i++) remain[i]=input[i]; remain[i]='\0'; analyse(); return 0;}
    四、实验结果4.1 运行截图
    五、实验总结本次实验基本完成了实验题目的要求,定义了一个文法,求出了每一个非终结符的VT集和LASTVT集,画出了算符优先关系表,并判定出给定的文法是否是算符优先文法。当给定一个 句子时,能够判定是否是文法中的句子,并能够将分析过程打印出来。
    这个实验最大的收获,不仅仅使我更进一步的了解到了算符优先算法是一个省略了所有单非终结符产生式对应的归约步骤,其分析效率是很高的,同时,通过此次实验让我的处理问题的能力,思考问题的角度都得到了很大的提高,从词法分析到语法分析再到语义分析,基本走完了编译器的大致流程,这对我理解编译的过程和具体的实现都是有极大的帮助的。
    0 留言 2019-04-24 15:22:39 奖励12点积分
  • 如何将本地项目部署到Github上

    Github上是一个面向开源及私有软件项目的托管平台那么如何上传项目至GitHub上:
    首先注册一个GitHub的账号
    然后,你需要下载安装GIT中(这里就不说了)
    第一步:创建新存储库
    在右上角,在您的头像或identicon旁边,单击然后选择新的存储库。
    填好后,单击创建存储库
    打开设置,有一个Github Pages的设置,点击源中的的无使其变成master分支,然后点击保存。
    刷新后,github pages设置框处,多了一行网址,这就是你的github pages的网址;选择一个盘,如E盘,右击空白处点击git bash here。输入如下命令:
    之后,输入git clone https://github.com/p525z/TbMis.git 。(注意克隆地址格式为:https://github.com/用户名/项目名.git )
    这时,你的e盘就会多一个演示文件,打开演示/ TbMis后会有一个README.md文件。
    将自己的项目文件复制至TbMis文件中

    执行:cd TbMis /执行:git status后会出现执行:git add .(别忘了加 .)
    之后执行如下命令,到这里,如果是第一次操作则需要你输入相关账号的信息:
    最后输入git pull和git push即可,如果是第一次操作则会有弹出框让你输入你的GitHub账号和密码,记得New SSH key。接下来就是等待啦。最后刷新GitHub页面进入刚才新建的那个仓库里面就会发现项目已经成功上传了。
    0 留言 2019-04-24 14:19:44 奖励5点积分
显示 30 到 45 ,共 15 条
eject