zoukankan      html  css  js  c++  java
  • WebKit浏览器内核源码分析-HTML解析模型图

    WebKit浏览器内核源码分析-HTML解析模型图

    摘要:

    通过分析WebKit的源代码,试图分析WebKit的内核设计架构,模块之间的关系,分析的时候以Qt的移植为参考,涉及移植的东西不多,主要还是以内核为主。在分析内核的时候,Frame是首当其冲的一个类,本文将分析Frame类的代码。

    浏览器的请求一般是以页面请求为单位,当用户通过网址输入一个URL,浏览器就开始一个页面请求。而一个页面请求可能包含一到多个页面子帧,以及图片、CSS和插件等派生子资源。Page类就是用来对应这样的页面请求。Page类是WebKit中非常重要的类,它就像内核对外的一个聚合器。

     

    1.    浏览器的请求概述

          浏览器的请求一般是以页面请求为单位,当用户通过网址输入一个URL,浏览器就开始一个页面请求。而一个页面请求可能包含有一到多个子帧,以及图片、CSS和插件等派生子资源。Page类就是用来对应这样的页面请求。前进后退,导航,编辑,右键菜单,设置,Inspector等这些用户参与的动作,大部分是同Page相关的。而标记语言解析、排版、加载则更多的同Frame相关

    我们通过几个图来看Qt移植中Page类同应用之间的关系。

     

    QWebPage通过QWebPagePrivate维护Page类的指针,并在QWebPagePrivate的构造函数中实例化Page对象。QWebPage类通过之后的createMainFrame调用实例化QWebFrame,而在QWebFrameData的构造函数中,以Page指针为参数调用了 Frame::create创建出 Frame对象

    1.    描述

    Frame类是WebCore内核同应用之间联系的一个重要的类。它有点像设计模式中的Façade,将内核的各个不同的零配件组装在了一起,但又不是Façade,因为用户很多时候还是要直接去操作里面的组件。除了设计上的考虑,Frame还有语法上的意义,它对应于Page里面的帧。

    2.    类结构

     

    1              FrameTree对象用来协助管理父帧和子帧的关系,常见的比如main frame之中有iframe元素,就会调用FrameLoaderClientQt::createFrame来产生子帧,产生的子帧会通过appendChild添加到主帧的树状结构中。Frame通过FrameTree对象,可以方便地访问它的父帧,子帧,兄弟帧。

    2              维护FrameLoader对象用来完成frame的加载,FrameLoader是一个非常重要的类,后续进行进一步的分析。

    3              维护NavigationScheduler对象用来管理页面跳转调度(比如重定向,meta refresh等)。

    4              DOMWindow用来管理同DOM相关的事件、属性和消息。

    5              FrameViwe类用于Frame的排版。

    6              Frame文档解析后,对每一个tag或者attr,会有对应的dom节点关联,Document类用来管理这些dom节点。不同的文档类型继承出不同的子类,比如HTML文档对应子类HTMLDocument,XML文档对应于XMLDocument

    7              SciptController对象,脚本控制器,用来管理脚本的执行和操作。

    8              Editor对象用来处理页面的编辑相关的操作,比如拷贝,粘贴,输入等,Editor对象,它同Page类的EditorClient对象紧密合作。和EditorClient的关系就如同PageFrame的关系。

    9              SelectionController用来管理Frame中的选取操作。

    10         AnimationControlle,动画控制,控制动画的播放,暂停,继续(同HTML video标签是否有关系?)

    11         EventHandler,事件处理对象,这里的对象主要是同上层应用也就是用户参与的事件,比如鼠标事件,按键事件(快捷键等),滚动事件,resize事件等。这是一个浏览器外壳经常需要打交道的类。

    3.    主要接口

    3.1   Create

    static PassRefPtr<Frame> create(Page*,HTMLFrameOwnerElement*,FrameLoaderClient*)
    描述: 调用Frame构造函数,创建出Frame对象。有两个地方会创建Frame对象,一是要加载一个新的页面请求,这个时候会创建main frame,一是在加载子帧的时候,通过FrameLoaderClientQtcreateFrame接口,创建子帧对应的Frame对象,在第一种情况中,HTMLFrameOwnerElement参数为NULL,第二种情况传子帧的父元素。在一个tab页内,main frame会重用。

    调用系列:

    àQwebPage::setView

    àQwebPage::setViewportSize

    àQwebPage::mainFrame

    àQwebPagePrivate::createMainFrame

    àQwebFrameData::QwebFrameData

    àFrame::create


    àFrameLoader::finishedLoading
    à……

    àHTMLDocumentParser::append

    à……

    àHTMLTreeBuilder::processToken

    à……

    àHTMLElementBase::openURL

    àSubFrameLoader::requestFrame

    à……

    àFrameLoaderClientQt::creatFrame

    àQwebFrameData::QwebFrameData

    àFrame::create

    3.2  createView

    void createView(const IntSize&, const Color&, bool, const IntSize&, bool,

                ScrollbarMode = ScrollbarAuto, bool horizontalLock = false,

                ScrollbarMode = ScrollbarAuto, bool verticalLock = false)

    描述:创建出FrameView对象,以用于之后的排版。应用调用这个函数的时候需要传入同排版有关的一些信息,如初始视窗大小,背景色,滚动条模式等。创建出FrameView以后,即调用Frame::setView设置成当前的FrameView
    函数调用系列:
    àFrameLoader::commitProvisionalLoad

    àFrameLoader::transitionToCommitted

    àFrameLoaderClientQt::transitionToCommittedForNewPage

    àFrame::createView

    3.3  setDocument

    void setDocument(PassRefPtr<Document>)

    描述:设置同Frame关联的Document对象(一般是DocumentWriter创建的)

    函数调用系列:

    àQWebFrame::QwebFrame

    àQwebFramePrivate::init

    àFrame::init

    àFrameLoader::init

    àDocumentWriter::begin

    àFrame::setDocument


    àDocumentLoader::receivedData
    àDocumentLoader::commitLoad

    àFrameLoaderClientQt::committedLoad

    àDocumentLoader::commitData

    àDocumentWriter::setEncoding

    àDocumentWriter::willSetEncoding

    àFrameLoader::receivedFirstData

    àDocumentWriter::begin

    àFrameLoader::clear

    àFrame::setDocument

    3.4  init

    void Frame::init

    描述:Frame对象初始化,会调用FrameLoader::init初始化FrameLoader对象。

    调用系列:
    àQWebFrame::QWebFrame

    àQwebFramePrivate::init

    àFrame::init

    3.5  setPageAndTextZoomFactors

    void setPageAndTextZoomFactors(float pageZoomFactor, float textZoomFactor)

    描述:设置页面放大因子和文字放大因子。在网页缩放或者改变网页字体大小的时候调用。

    WebKit内核源代码分析(二)

    版权

    摘要:本系列通过分析WebKit的源代码,试图分析WebKit的内核设计架构,模块之间的关系,分析的时候以Qt的移植为参考,涉及移植的东西不多,主要还是以内核为主。FrameLoader类负责一个Frame的加载,在Frame的流程中起到非常重要的重要,同很多组件都有交互,本文将分析FrameLoader类的代码。 
    1. 概述 

    顾名思义,FrameLoader是一个Frame的loader,它的作用就是为客户提供一个下载一个Frame的一系列接口。这里的客户指的是类的客户,比如Frame类,间接客户是上层应用,比如qwebframe。 
    从它的定义看,最容易想到的是一个load接口,用来将一个frame load下来。任何一个页面至少都需要一个mainframe,因此一个页面的下载一般就是从load一个mainframe开始。 
    load frame的过程中,通过FrameLoaderClient接口将load过程的不同阶段告知客户。 
    FrameLoader通过setDocumentLoader相当于把load的工作委托给了DocumentLoader类。 
    FrameLoader同DocumentLoader是has-a的关系。一般在load的时候创建DocumentLoader。Frame调用DocumentLoader的startLoadingMainResource开始load frame。 
    2. 类关系 


    [img]http://dl.iteye.com/upload/attachment/437204/38586d86-f8fa-3023-b30f-e25e2e2a640a.jpg[/img] 


    1)Frame和FrameLoader是contain-a的关系,在Frame的构造函数中调用FrameLoader的构造函数,调用时传入参数Frame指针和FrameLoaderClient指针。 
    2)Frame有可能有子Frame,所以维护SubFrameLoader对象m_subframeLoader来管理子Frame的load。Frame可以对应xml document,也可对应html document,等等。跟Document相关的子resource的load不在FrameLoader的职责范围内。 
    3)包含一个DocumentWriter类对象m_writer,当Frame的数据load finish的时候,将数据传给DocumentWriter类,进行下一步的处理(比如解码) 
    4)FrameLoader维护了三个DocumentLoader对象,分别对应于不同的阶段,m_policyDocumentLoader对应于收到用户load调用,进行policy check阶段,m_provisionalDocumentLoader对应于policy check通过以后,Frame数据还没有到来之前,它会负责startLoadingMainResource的调用。m_documentLoader则是Frame第一个数据到来以后使用的DocumentLoader,这个时候,前一个主Frame的DocumentLoader已经不能再用(user agent开始白屏,刷掉前一个页面的显示)。 
    5)包含一个HistoryController对象,用于操作历史记录相关的接口,保存或者恢复Document和View相关的状态,维护前进后退队列,以实现前进后退功能,前进后退本质上是同Page对象关联的,FrameLoader通过HistoryController操作m_backFowardController对象 
    6)包含一个ResourceLoadNotifier对象,主要用于同ResourceLoader及FrameLoaderClient打交道,可以理解为ResourceLoader有事件变化或者发生的时候,通知FrameLoader的一个手段 
    7)包含一个SubframeLoader对象,当FrameLoader下载的Document有子帧需要请求的时候(比如HTMLDocument中解析到iframe 元素),用来处理子帧请求 
    8)将FrameLoader的状态封装到FrameLoaderStateMachine中,这个状态同FrameState不同,FrameState倾向于判断Frame涉及的Document的下载状态,是出于发起状态(Provisional),还是出于已经收到响应但不全(CommittedPage),还是响应收全的状态,倾向于同http相关。而FramLoaderStateMachine倾向于同DocumentLoader相关,用来描述FrameLoader处理DocumentLoader的节点,是处于已经创建,还是显示的状态。 
    9)PolicyChecker主要用来对FrameLoader进行一些校验。包括三种校验:NewWindow,Navigation和Content。NewWindow对应于浏览器需要新开一个tab页或窗口的时候,Navigation对应于一个页面请求发起的时候,Content校验对应于收到数据以后(判断Mime type等),PolicyChecker通过提供对应的接口,由FrameLoaderClient来对这些请求进行校验,以确定是否允许继续,或者需要其它的动作。 

    3. 主要接口 
    Frame::init 

    功能:FrameLoader的初始化 

    函数调用系列 
    àQWebFrame::QWebFrame(QwebPage* parent,QWebFrameData *frameData) 
    àQWebFramePrivate::init(QWebFrame* qwebframe,QWebFrameData* frameData) 
    àFrame::init() 
    àFrameLoader::init() 

    说明:主要做一些自身的初始化工作,比如初始化状态机,Sandbox Flags,创建DocumentLoader被设置为Policy DocumentLoader和Provisional DocumentLoader,调用DocumentLoader和documentWriter等的接口进行初始化操作 
    FrameLoader::commitProvisionalLoad 

    功能:提交Provisional阶段下载的数据 

    函数调用系列: 
    àDocumentLoader::finishLoading 
    àDocumentLoader::commitIfReady 
    àFrameLoader::commitProvisionalLoad 

    或者 
    àResourceLoader::didReceiveData 
    àMainResourceLoader::addData 
    àDocumentLoader::receiveData 
    àDocumentLoader::commitLoad 
    àDocumentLoader::commitIfReady 
    àDocumentLoader::commitProvisionalLoad 

    说明:这个接口主要的操作是将Provisional DocumentLoader设置成DocumentLoader,因为已经收到数据,所以FrameState也会跃迁到FrameStateCommittedPage。还有历史记录,PageCache相关的操作。另外,这个接口会间接调用FrameLoaderClientQt::transitionToCommittedForNewPage,通过Frame::createView创建出FrameView来。 
    Frame::finishedLoading 

    功能:frame请求网络加载完成的时候调用此接口 

    函数调用系列 
    àResourceLoader::didFinishLoading 
    àMainResourceLoader::didFinishLoading 
    àFrameLoader::finishedLoading 
    àFrameLoader::init() 

    说明:检查是否有网络错误,告诉DocumentLoader和DocumentWriter下载完成,以便进行后续操作(提交数据,解析)。 
    FrameLoader::finishedParsing 

    功能:解析完成调用此接口 

    函数调用系列 
    àDocumentWritter::end 
    à…. 
    àDocument::finishParsing 
    à…. 
    àDocument::finishedParsing 
    àFrameLoader::finishedParsing 
    FrameLoader::load(const ResourceRequest& request,bool lockHistory) 

    功能:加载一个frame请求,Frame请求相关的数据,封装成ResourceRequest传入。 

    函数调用系列:一般由应用触发调用 

    说明:这个接口调用FrameLoaderClientQt::createDocumentLoader创建出DocumentLoader,并以此DocumentLoader为Policy Document Loader,进入Policy check流程。

     

     

    WebKit内核源代码分析(三)浏览器的请求

    版权

    摘要:浏览器的请求一般是以页面请求为单位,当用户通过网址栏输入一个url,浏览器就开始一个页面请求。而一个页面请求可能包含有一到多个页面子帧,以及图片、CSS和插件等派生子资源。Page类就是用来对应这样的页面请求。Page类是WebKit中非常重要的一个类,它就像内核对外的一个聚合器。

    关键词:WebKit内核源代码,WebCore,Page,FrameWebKit架构

    1.    概述

    浏览器的请求一般是以页面请求为单位,当用户通过网址栏输入一个url,浏览器就开始一个页面请求。而一个页面请求可能包含有一到多个页面子帧,以及图片、CSS和插件等派生子资源。Page类就是用来对应这样的页面请求。前进后退,导航,编辑,右键菜单,设置,Inspector等这些用户参与的动作,大部分是同Page相关的。而标记语言解析、排版、加载则更多地同Frame相关。

    我们通过几个图来看下Qt移植中Page类同应用之间的关系。

     

     

     

     

    QWebPage通过QWebPagePrivate维护Page类的指针,并在QWebPagePrivate的构造函数中实例化Page对象。QWebPage类通过之后的createMainFrame调用实例化QwebFrame,而在QwebFrameData的构造函数中,以Page指针为参数调用了Frame::create 创建出Frame对象。

     

     

      

     Page类通过组合其它类的方式,实现了很多功能,Page类本身并没有多少代码。

    2.    类关系

     

    2.1  PageGroup

    PageGroup并不是用来对Page进行管理的,而是设计用来将一些具有共同的属性或者设置的Page编成组的,以方便对这些属性进行管理。目前这样的属性包括localStorage的属性,IndexDBUser ScriptUser StyleSheet等。最常见的同PageGroup相关的操作是维护已访问链接(如addVisitedLink等接口)。根据地瓜的理解,假设WebKit内核之上架设多个应用(浏览器是一个应用),比较可能的是,一个应用独立一个PageGroup。这里同多tab页没有关系,多tab页属于同一个PageGroup。地瓜曾在mailing group上就这个问题咨询过,一位RIM的同学给我举了一个例子,比如一个基于WebKit的邮件程序,一方面他可能调用基于webkitbrowser来显示网页,另外他本身也基于webkit来显示一些邮件,这两个之间的setting有很大可能不一样,他们就使用不同的PageGroup

    PageGroup中有这个Group已经安装并且使用的User ScriptUser StyleSheet的集合,一般在网页解析完毕后,这些User ScriptUser StyleSheet会插入到Document中。

    PageGroup中还维护了Local StorageIndex DB相关的设置,比如它们的Path,上限等,通过GroupSettings类实现。

    PageGroup创建以后,每次创建一个新的Page对象,会通过addPage接口加入到这个PageGroupm_pages中。

    每次有导航行为发生的时候,会调用addVisitedLink来将url加入到已访问链接中。如果浏览器要跟踪已访问的接口,则在初始化的时候必须调用PageGroup::setShouldTrackVisitedLinks,且参数为true。此处shouldTrackVisitedLinks是一个静态的全局变量,也就是说,所有应用维护一样的行为(一个应用将其设置为false会影响到其它同样基于此核的应用)?

    Page类中维护了PageGroup的指针,并提供了group接口,这是个lazy接口,如果m_group不存在,会调用InitGroup来创建一个。对于Page类来说,如果没有设置GroupName,则在初始化的时候会生成一个空GroupNamePageGroup,由m_singlePageGroup维护,并把指针赋给m_group,如果以非空的名字调用了setGroupName,则会重新创建PageGroup,此时这个PageGroupm_group来维护。

    2.2  Setting

    WebCore中的设置相关的类,浏览器应用的不少配置、选项同该类相关,Qt移植中,应用在创建Page对象后,会根据Page::settings来实例化QwebSetting

    2.3  Chrome

    原生窗口接口类,参考地瓜写的”WebKit中的ChromeChromeClient”

    2.4  其它

    SelectionController :负责管理Page中的选取操作,绝大部分选取操作是基于Frame的,只在FrameSelection为空的时候,对焦点游标的绘制工作才会使用到Page类的SelectionController

    SharedGraphicsContext3D:共享3D图形上下文,为了优化2D显示而加入。在加速的2D canvas中,引入的DrawingBuffer的概念,SharedGraphicsContext3D提供了createDrawingBuffer来创建DrawingBuffer

    DragController:拖拽控制器。Chrome的超级拖拽功能同这个有关?地瓜会在以后对此进行求证。

    FocusController:焦点控制器。考虑到焦点会在各个frame之间切换,所以由Page类维护焦点控制器最合适不过。

    ContextMenuController:右键下拉菜单控制器。

    InspectorController:Inspector控制器,浏览器中的很多开发工具都同这个类相关。

    GeolocationController:定位定位服务控制器。

    DeviceMotionController:设备移动控制器

    DeviceOrientationController:设备方向控制器

    SpeechInputClient:语音输入Client

    ProgressTracker:进度跟踪。

    BackForwardController:前进后退操作控制。

    Frame:一个Page由至少一个主帧和若干个其它子帧构成。

    HistoryItem:历史记录。

    PluginData:插件相关,未来可能同PluginDatabase类合并。主要是初始化Plugin的信息。

    PluginHalter:用来控制Plugin的停止和重新开始。

    RenderTheme:这个类提供了控件的渲染和绘制接口。Qt移植中,RenderThemeQtRenderTheme接口的具体实现。

    EditorClient:同编辑功能相关,比如拷贝、剪切、删除等操

     

     

     

    WebKit 内核源代码分析 (  )

    摘要:本文介绍 WebCore  Loader 模块是如何加载资源的,分主资源和派生资源分析loader 模块的类关系。

    WebKit 内核源代码分析 HTML解析模型

    关键词: WebKit,Loader,Network,ResouceLoader,SubresourceLoader

    一、类结构及接口

    Loader 模块是 Network 模块的客户。 Network 模块提供指定资源的获取和上传功能,获取的资源可能来自网络、本地文件或者缓存。对不同 HTTP 实现的适配会在Network 层完成,所以 Loader 接触到的基本上是同 OS  HTTP 实现无关的 Network 层接口。

     

     

     

    如上是 Loader  NetWork 之间的类关系图, ResourceHandleClient ResourceHandle 的客户,它定义一系列虚函数,这些虚函数是 ResouceHandle 的回调,继承类实现这些接口。

    ResouceHandleClient 的接口同网络传输过程息息相关,一般为某一个网络事件对应的回调。下面是其中的一些接口。

    // 一般情况下,在发起网络请求前调用,可以设置特定的 Http

    头部,比如 user agent 等,在重定向请求的时候,也会自动调

    void willSendRequest(ResourceHandle*, ResourceRequest&, const

    ResourceResponse&)

    // 上传数据的时候,在 TCP wrtie 事件的时候,向对方发送数据的

    时候调用, loader 可以根据这个回调显示上传进度。

    void didSendData(ResourceHandle*, unsigned long long

    /*bytesSent*/, unsigned long long /*totalBytesToBeSent*/)

    // 收到第一个响应包,此时至少 http 的部分头部已经解析(如

    status code ), loader 根据响应的头部信息判断请求是否成功

    等。

    void didReceiveResponse(ResourceHandle*,

    const   ResourceResponse&)

    // 收到 HTTP 响应数据,类似 tcp  read 事件,来 http 响应数据

    了, Network 的设计机制是来一段数据上传一段数据。

    void didReceiveData(ResourceHandle*, const char*, int,

      int /*lengthReceived*/)

        // 加载完成,数据来齐。

    void didFinishLoading(ResourceHandle*, double /*finishTime*/)

    // 加载失败

    void didFail(ResourceHandle*, const ResourceError&)

    // 要求用户鉴权

    void didReceiveAuthenticationChallenge(ResourceHandle*,

    const AuthenticationChallenge&)

    WebCore 把要加载的资源分成两类,一类是主资源,比如 HTML 页面,或者下载项,一类是派生资源,比如 HTML 页面中内嵌的图片或者脚本链接。这两类资源对于回调的处理有很大的不同,比如,同样是下载失败,主资源可能需要向用户报错,派生资源比如页面中的一张图下载失败,可能就是图不显示或者显示代替说明文字而已,不向用户报错。因此有了 MainResourceLoader  SubresourceLoader 之分。它们的公共基类ResourceLoader 则完成一些两种资源下载都需要完成的操作,比如通过回调将加载进程告知上层应用。

    ResourceLoader 通过 ResourceNotifier 类将回调传导到 FrameLoaderClient 类。

     

     
       

            

    主资源的加载是立刻发起的,而派生资源则可能会为了优化网络,在队列中等待 (这里的立刻发起是 loader 层面的,不是 Network 层面的 )  ResourceScheduler 这个类就是用来管理资源加载的调度。主要调度对象就是派生资源,会根据 host 来影响资源加载的先后顺序。

    主资源和派生资源的加载还有一个区别,主资源目前是没有缓存的,而派生资源是有缓存机制的。这里的缓存指的是 Resouce Cache ,用于保存原始数据(比如 CSS  JS等),以及解码过的图片数据,通过 Resource Cache 可以节省网络请求和图片解码的时候。不同于 Page Cache  Page Cache 存的是 DOM 树和 Render 树的数据结构,用来在前进后退的时候快速显示页面。

    二、加载流程

        下图是加载 html 页面时,一个正常的加载流程。

     

     

    三、主资源加载过程

    1.        DocumentLoader 调用 MainResourceLoader::load  loader 发起请求

    2.        调用 MainResourceLoader::loadNow

    3.        调用 MainResourceLoader::willSendRequest

    4.        调用 ResourceLoader::willSendRequest,  callback 通过 ResourceNotifier 传导给FrameLoaderClient  Client 可以在回调中操作 ResourceRequest ,比如设置请求头部。

    5.        调用 PolicyChecker::checkNavigationPolicy 过滤掉重复请求等

    6.        loader 调用 ResourceHandle::create  Network 发起加载请求

    7.        收到第一个 HTTP 响应数据包 ,Network 回调MainResourceLoader::didReceiveResponse ,主要处理 HTTP 头部。

    8.        调用 PolicyChecker:: checkContentPolicy, 并最终通过 FrameLoaderClient dispatchDecidePolicyForMIMEType 判断是否为下载请求(存在 "Content-Disposition"http 头部)

    9.        调用 MainResourceLoader::continueAfterContentPolicy ,根据 ResourceResponse检测是否发生错误。

    10.   调用 ResourceLoader::didReceiveResponse ,将 callback 通过 ResourceNotifier 传导给 FrameLoaderClient 

    11.   收到 HTTP 体部数据,调用 MainResourceLoader::didReceiveData

    12.   调用 ResourceLoader::didReceiveData ,将 callback 通过 ResourceNotifier 传导给FrameLoaderClient

    13.   调用 MainResourceLoader::addData

    14.   调用 DocumentLoader::receivedData

    15.   调用 DocumentLoader::commitLoad

    16.   调用 FrameLoader::commitProvisionalLoad  FrameLoader  provisional 状态跃迁到 Committed 状态

    17.   调用 FrameLoaderClientQt::committedLoad

    18.   调用 DocumentLoader::commitData ,启动 Writer 对象来处理数据(DocumentWriter::setEncoding  DocumentWriter::addData 

    19.   调用 DocumentWriter::addData

    20.   调用 DocumentParser::appendByte

    21.   调用 DecodedDataDocumentParser::appendBytes 对文本编码进行解码

    22.   调用 HTMLDocumentParser::append ,进行 HTML 解析

    23.   数据来齐,调用 MainResourceLoader::didFinishLoading

    24.   调用 FrameLoader::finishedLoading

    25.   调用 DocumentLoader::finishedLoading

    26.   调用 FrameLoader::finishedLoadingDocument ,启动 writer 对象接收剩余数据,重复 19-22 进行解析

    27.   调用 DocumentWriter::end 结束接收数据(调用 Document::finishParsing 

    28.   调用 HTMLDocumentParser::finish

    四、派生资源加载流程

      在派生资源的加载中, SubresourceLoader 更多起到的是一个转发的作用,通过它的client  SubresourceLoaderClient 类)来完成操作。

     

     

     

       各个加载阶段的处理在 SubresourceLoaderClient 的派生类CachedResourceRequest,Loader,IconLoader 中完成。 Client 会创建 SubresourceLoader 

    请求发起阶段, ResourceLoadScheduler 负责对 SubresourceLoader 进行调度。

     

     

       Document 类会创建 CachedResourceLoader 类的对象 m_cachedResourceLoader, 这个类 ( 对象 ) 提供了对 Document 的派生资源的访问接口 requestImage requestCSSStyleSheet  requestUserCSSStyleSheet  requestScript  requestFont requestXSLStyleSheet  requestLinkPrefetch 。为了实现这些接口, CachedResourceLoader需要创建 CachedResourceRequest 对象来发起请求。

    一般情况下,一个 Document 拥有一个 CachedResourceLoader 类实例。

    MemoryCache 类则对提供缓存条目的管理,可以方便地进行 add  remove ,缓存淘汰等。具体的缓存条目则是通过 CachedResource 类存储, MemoryCache 类维护了一个 HashMap 存储所有缓存条目。

    HashMap <String,CachedResource> m_resources;

    CachedResourceRequest 依赖于 CachedResource,  CacheResourceRequest 的构造函数中,会传入 CachedResource 对象作为参数。 CachedResource 既存储响应体部,也存储同 cache 相关的头部。在发起请求前,会检查是否有 cache  validator ,在收到响应的时候,则需要更新对应的头部。 CachedResource 类实现了 RFC2616 中的缓存一节。实际上 CachedResource 类真正完成了同网络的通信。 CachedResource 类根据申请的资源类型派生出不同的子类。

     

     

          CachedResource 类的使用者必须是 CachedResourceClient, 在这个类中维护了CachedResourceClient 类的集合 m_clients 。每一个 Client 通过 addClient  removeClient将自己加入到该类的 Client 集合中。 CachedResourceClientWalker 则提供了CachedResouceClient 的一个遍历接口。当数据来齐的时候, CachedResource 类会通过CachedResouceClient::notifyFinished 接口通知使用者。

    下图是 Image 元素对应的几个类关系。

     

     

     

    下面以 image 为例分析其加载过程

    1.        解析 html 页面的时候,解析到 img 标签,调用 HTMLImageElement::create 创建 HTMLImageElement 对象,该对象包含 HTMLImageLoader 对象m_imageLoader

    2.        解析到 img  href 属性,调用ImageLoader::updateFromElementIgnoringPreviousError

    3.        调用 ImageLoader::updateFromElement

    4.        调用 CachedResourceLoader::requestImage

    5.        调用 CachedResourceLoader::requestResource( 根据缓存的情况确定是否可以从缓存获取,或者需要 revalidate ,或者需要直接从网络获取 )

    6.        调用 CachedResourceLoader::loadResource

    7.        根据 Resource 的类型调用 createResource 创建对应的 CachedResource

    8.        调用 MemoryCache::add  cache 中查找是否有对应的 cache 条目,如果没有创建之

    9.        调用 CachedImage::load

    10.   调用 CachedResource::load

    11.   调用 CachedResourceLoader::load

    12.   调用 CachedResourceRequest::load

    13.   创建 CachedResourceRequest 对象,它将作为 SubresourceLoader  client

    14.   调用 ResourceLoaderScheduler::scheduleSubresourceLoad

    15.   调用 SubresourceLoader::create

    16.   ResourceLoadScheduler::requestTimerFired

    17.   调用 ResourceLoader::start

    18.   调用 ResourceHandle::create 发起请求

    19.   收到 HTTP 响应头部,调用 ResourceLoader::didReceiveResponse

    20.   调用 SubresourceLoader::didiReceiveResponse

    21.   调用 CachedResourceRequest::didReceiveResponse 处理响应头部,特别是同缓存相关的头部,比如 304  status code

    22.   调用 ResourceLoader::didReceiveResponse

    23.   收到体部数据,调用 ResourceLoader::didReceiveData

    24.   调用 SubresourceLoader::didReceiveData

    25.   调用 ResourceLoader::didReceiveData

    26.   调用 ResourceLoader::addData 将数据存储到 SharedBuffer 里面

    27.   调用 CachedResourceRequest::didReceiveData

    28.   数据来齐 , 调用 ResourceLoader::didFinishLoading

    29.   调用 SubresourceLoader::didFinishLoading

    30.   调用 CachedResourceRequest::didFinishLoading

    31.   调用 CachedResource::finish

    32.   调用 CachedResourceLoader::loadDone

    33.   调用 CachedIm

    WebKit内核源代码分析()

     

    1.    HTML解析模型-HTML解析模型图

                                                                                   

    1 HTML解析模型图

    上图是HTML解析模型图,HTML解析分成TokeniserTree Construction两个步骤,在”WebKit中的html词法分析

    (http://blog.csdn.net/dlmu2001/archive/2010/11/09/5998130.aspx)一文中,我们已经对Tokeniser这一步进行了分析,本文的目标是Tree Construction这一步。

    Tree Construction输入是token流,输出是DOM节点树。

    2.    DOM

    HTML DOM定义了一套标准来将html文档结构化,它定义了表示和修改文档所需的对象、这些对象的行为和属性以及对象之间的关系,可以把它理解为页面上数据和结构的一个树形表示。

    NodeDOM模型中的基础类,它可以分成13类(见NodeType),在HTML解析中,最常见的是DocumentElementText三类。

    l  Document是文档树的根节点,在HTML文档中,他派生为HTMLDocument

    l  在文档中,所有的标签转化为Element类,一般它有标签名,并根据标签名继承为特定的子类。

    l  Element之间的原始文本转化成Text类。

    以一个简单的html页面为例:

    <html>

    <head>

    <title>test</title>

    </head>

    <body>

    <h1>hl1</h1>

    <h2>hl2</h2>

    <h3>hl3</h3>

    </body>

    </html>

    经过解析后的节点树如下(忽略换行符):

     

    2 HTML DOM节点树示例

    如果没有忽略换行符,则每个换行符就是一个Value” ”Text节点。

    3.    Tree Construction原理

    将图二中的节点树以WebKit中的类具体化(同样忽略换行符)。

     

    3 Webkit HTML DOM节点树示例

    看到这里,你是不是觉得仿佛看到了一个呼之欲出的Tree Construction轮廓?是的,最简化的情况就是这样,根据输入的token,创建出相应的Element派生类,然后添加到DOM树中合适的位置,这就是Tree Construction干的事情。当然,添加到合适的位置,这个需要一系列复杂的规则,另外,WebKitRender树的创建也放到了Tree Construction阶段中来,再加上CSSJavascript,所以,这就是你看到的复杂的代码。

    放出两个函数原型,热热身,培养培养感情。

    PassRefPtr<Element> HTMLConstructionSite::createHTMLElement(AtomicHTMLToken& token);

    void HTMLConstructionSite::insertHTMLElement(AtomicHTMLToken& token); 

    Tree Construction流程由一个状态Insertion Mode”进行控制,它影响token的处理以及是否支持CDATA部分,HTML5中给出了详细的规则(http://www.whatwg.org/specs/web-apps/current-work/multipage/parsing.html#the-insertion-mode)。它也控制了在特定状态下能够处理的token,比如在head里面,再出现head标签,显然是不应该处理的。

    4.    开放元素堆栈

    为了维护即将解析的标签同已解析的标签之间的关系(此时即将解析的标签还没有加入到DOM树中),引入了开放元素堆栈m_openElements,初始状态下,这个堆栈是空的,它是向下增长的,所以最上面的节点是最早加入到堆栈中的,在html文档中,最上面的节点就是html元素,最底部的节点就是最新加入到堆栈中的。Tree Builder的时候,每碰到一个StartTagtoken,就会往m_opnElements中压栈,碰到EndTagtoken,则出栈。像Character这样的token,则不需要进行压栈出栈的动作,只有可以包含子节点的tag,才做压栈出栈的动作。Html5的文档中对开放元素堆栈也有说明,http://www.whatwg.org/specs/web-apps/current-work/multipage/parsing.html#the-stack-of-open-elements

    对于正在解析的token,除了根节点html,它必然是堆栈底部元素(m_openElements.top())的子节点,所以在形成DOM树的时候,就可以通过ContainerNode::parserAddChild这样的接口加入到DOM节点树中。

    除了正常的堆栈和压栈,对于html,head,body元素,栈结构(HTMLElementStack)中有专门的成员m_htmlElement,m_headElement,m_bodyElement记录,主要是用于检错纠错处理。

    在本文的html范例中,当解析到<h2>hl2</h2>hl2这个charactertoken的时候,它的开放元素堆栈如下,HTMLHeadingElement是堆栈的top,所以它是hl2这个Text节点的parent

     

    开放元素堆栈示例

    此时的DOM节点树如下:

     

    5 Webkit DOM节点数示例

    5.    元素的创建

    HTMLElementFactory类提供了元素的创建方法createHTMLElement。传入为对应的标签名,所属的document,所属的form(如果属于form),在parser的时候,最后一个参数为true

    PassRefPtr<HTMLElement> HTMLElementFactory::createHTMLElement(const QualifiedName& qName, Document* document, HTMLFormElement* formElement, bool createdByParser);

    HTMLElementFactory中,通过一个Hash Maptag name和对应的元素构造函数对应起来(gFunctionMap)tag一般对应一个派生于HTMLElement的类。如下是HTMLHeadingElement的类层次结构图。

     

    6 HTMLHeadingElement类层次图

    6.    其它

    HTMLConstructionSite::attach中的attach一词,地瓜理解主要是attachDOM节点数上,当然,它同时调用了Element::attachElement类的attach主要是attachRender树上,它会创建对应该ElementRendrObject

    除了m_openElementsHTMLConstructionSite同时维护了Format 元素列表m_activeFormattingElements,Formating元素就是那些格式化标签,包括a,b,big,code,em,font,I,fot,I,nobr,s,small,strike,strong,tt,u。为了处理这些Formatting元素的嵌套关系(此时它们可能不是父子关系,而是平级,不加入到m_openElements),HTML5引入了这个列表(http://www.whatwg.org/specs/web-apps/current-work/multipage/parsing.html#list-of-active-formatting-elements)。

    使用gdb调试的童子,可以运行Tools/gdb/webkit.py脚本,在print结构体的时候得到易于理解的表示,还可以打印出节点树,具体参考http://trac.webkit.org/wiki/GDB

    HTTP请求头概述 (HttpServletRequest)

     


       HTTP客户程序(例如浏览器),向服务器发送请求的时候必须指明请求类型(一般是GET或者POST)。如有必要,客户程序还可以选择发送其他的请求 头。大多数请求头并不是必需的,但Content-Length除外。对于POST请求来说Content-Length必须出现。
     下面是一些最常见的请求头

       Accept:浏览器可接受的MIME类型。

       Accept-Charset:浏览器可接受的字符集。

       Accept-Encoding:浏览器能够进行解码的数据编码方式,比如gzipServlet能够向支持gzip的浏览器返回经gzip编码的HTML页面。许多情形下这可以减少510倍的下载时间。

       Accept-Language:浏览器所希望的语言种类,当服务器能够提供一种以上的语言版本时要用到。

       Authorization:授权信息,通常出现在对服务器发送的WWW-Authenticate头的应答中。

        Connection:表示是否需要持久连接。如果Servlet看到这里的值为“Keep-Alive”,或者看到请求使用的是HTTP 1.1HTTP 1.1默认进行持久连接),它就可以利用持久连接的优点,当页面包含多个元素时(例如Applet,图片),显著地减少下载所需要的时间。要实现这一 点,Servlet需要在应答中发送一个Content-Length头,最简单的实现方法是:先把内容写入 ByteArrayOutputStream,然后在正式写出内容之前计算它的大小。

       Content-Length:表示请求消息正文的长度。

       Cookie:这是最重要的请求头信息之一

       From:请求发送者的email地址,由一些特殊的Web客户程序使用,浏览器不会用到它。

       Host:初始URL中的主机和端口。

       If-Modified-Since:只有当所请求的内容在指定的日期之后又经过修改才返回它,否则返回304“Not Modified”应答。

       Pragma:指定“no-cache”值表示服务器必须返回一个刷新后的文档,即使它是代理服务器而且已经有了页面的本地拷贝。

       Referer:包含一个URL,用户从该URL代表的页面出发访问当前请求的页面。

       User-Agent:浏览器类型,如果Servlet返回的内容与浏览器类型有关则该值非常有用。

       UA-PixelsUA-ColorUA-OSUA-CPU:由某些版本的IE浏览器所发送的非标准的请求头,表示屏幕大小、颜色深度、操作系统和CPU类型。

      有关HTTP头完整、详细的说明,请参见http://www.w3.org/Protocols/HTTP规范。

    HTTP应答头概述(HttpServletResponse
        Web服务器的HTTP应答一般由以下几项构成:一个状态行,一个或多个应答头,一个空行,内容文档。设置HTTP应答头往往和设置状态行中的状态代码结 合起来。例如,有好几个表示文档位置已经改变的状态代码都伴随着一个Location头,而401Unauthorized)状态代码则必须伴随一 个WWW-Authenticate头。

       然而,即使在没有设置特殊含义的状态代码时,指定应答头也是很有用的。应答头可以用来完成:设置Cookie,指定修改日期,指示浏览器按照指定的间隔刷新页面,声明文档的长度以便利用持久HTTP连接,……等等许多其他任务。

       设置应答头最常用的方法是HttpServletResponsesetHeader,该方法有两个参数,分别表示应答头的名字和值。和设置状态代码相似,设置应答头应该在发送任何文档内容之前进行。

       setDateHeader方法和setIntHeadr方法专门用来设置包含日期和整数值的应答头,前者避免了把Java时间转换为GMT时间字符串的麻烦,后者则避免了把整数转换为字符串的麻烦。

       HttpServletResponse还提供了许多设置

    setContentType:设置Content-Type头。大多数Servlet都要用到这个方法。
    setContentLength:设置Content-Length头。对于支持持久HTTP连接的浏览器来说,这个函数是很有用的。
    addCookie:设置一个CookieServlet API中没有setCookie方法,因为应答往往包含多个Set-Cookie头)。
    另外,如上节介绍,sendRedirect方法设置状态代码302时也会设置Location头。

       有关HTTP头详细和完整的说明,请参见http://www.w3.org/Protocols/规范。

    HTTP应答头 说明
    Allow 服务器支持哪些请求方法(如GETPOST等)。

    Content-Encoding 文档的编码(Encode)方法。只有在解码之后才可以得到Content-Type头指定的内容类型。利用gzip压缩文档能够显著地减少HTML文档 的下载时间。JavaGZIPOutputStream可以很方便地进行gzip压缩,但只有Unix上的NetscapeWindows上的IE 4IE 5才支持它。因此,Servlet应该通过查看Accept-Encoding头(即request.getHeader("Accept- Encoding"))检查浏览器是否支持gzip,为支持gzip的浏览器返回经gzip压缩的HTML页面,为其他浏览器返回普通页面。

    Content-Length 表示内容长度。只有当浏览器使用持久HTTP连接时才需要这个数据。如果你想要利用持久连接的优势,可以把输出文档写入 ByteArrayOutputStram,完成后查看其大小,然后把该值放入Content-Length头,最后通过 byteArrayStream.writeTo(response.getOutputStream()发送内容。

    Content-Type 表示后面的文档属于什么MIME类型。Servlet默认为text/plain,但通常需要显式地指定为text/html。由于经常要设置 Content-Type,因此HttpServletResponse提供了一个专用的方法setContentTyep

    Date 当前的GMT时间。你可以用setDateHeader来设置这个头以避免转换时间格式的麻烦。

    Expires 应该在什么时候认为文档已经过期,从而不再缓存它?

    Last-Modified 文档的最后改动时间。客户可以通过If-Modified-Since请求头提供一个日期,该请求将被视为一个条件GET,只有改动时间迟于指定时间的文 档才会返回,否则返回一个304Not Modified)状态。Last-Modified也可用setDateHeader方法来设置。
    Location 表示客户应当到哪里去提取文档。Location通常不是直接设置的,而是通过HttpServletResponsesendRedirect方法,该方法同时设置状态代码为302

    Refresh 表示浏览器应该在多少时间之后刷新文档,以秒计。除了刷新当前文档之外,你还可以通过setHeader("Refresh", "5; URL=http://host/path")让浏览器读取指定的页面。注意这种功能通常是通过设置HTML页面HEAD区的<META HTTP-EQUIV="Refresh" CONTENT="5;URL=http://host/path">实现,这是因为,自动刷新或重定向对于那些不能使用CGIServletHTML编写者十分重要。但是,对于Servlet来说,直接设置Refresh头更加方便。注意Refresh的意义是“N秒之后刷新本页面或访问指定 页面,而不是每隔N秒刷新本页面或访问指定页面。因此,连续刷新要求每次都发送一个Refresh头,而发送204状态代码则可以阻止浏览器继续刷 新,不管是使用Refresh头还是<META HTTP-EQUIV="Refresh" ...>。注意Refresh头不属于HTTP 1.1正式规范的一部分,而是一个扩展,但NetscapeIE都支持它。

    Server 服务器名字。Servlet一般不设置这个值,而是由Web服务器自己设置。

    Set-Cookie 设置和页面关联的CookieServlet不应使用response.setHeader("Set-Cookie", ...),而是应使用HttpServletResponse提供的专用方法addCookie。参见下文有关Cookie设置的讨论。

    WWW-Authenticate 客户应该在Authorization头中提供什么类型的授权信息?在包含401Unauthorized)状态行的应答中这个头是必需的。例 如,response.setHeader("WWW-Authenticate", "BASIC realm=/"executives/"")。注意Servlet一般不进行这方面的处理,而是让Web服务器的专门机制来控制受密码保护页面的访问 (例如.htaccess)。

    HTTP请求解析过程 (简单概括)

    1.域名解析

     用户输入网址,由域名系统DNS解析输入的网址;

    2.TCP3次握手

     通过域名解析出的IP地址来向web服务器发起TCP连接请求,如果3次握手通过,则与web服务端建立了可靠的连接;

    3.发送http请求

     web客户端向web服务端发送请求;

    4.接收http响应

     web客户端接收来自web服务端的响应,包含各种根据请求反馈的数据;

    5.web客户端(浏览器)解释响应

     最后由浏览器解析响应里的数据,即HTML代码,以及HTML代码中请求的资源,然后由浏览器呈现给用户。

    以上就是对一个HTTP请求网页的解析过程的简单概括

    HTTP——概述、请求和响应、GETPOST请求

    HTTP协议概述

    WEB浏览器与WEB服务器之间的一问一答的交互过程必须遵循一定的规则,这个规则就是HTTP

    HTTPhypertext transfer protocol(超文本传输协议)的简写,它是TCP/IP协议之上的一个应用层协议,用于定义WEB浏览器与WEB服务器之间交换数据的过程以及数据本身的格式。

    1.HTTP协议到底约束了什么:

        1.约束了浏览器以何种格式向服务端发生数据:

        2.约束了服务器应该以何种格式来接受客户端发送的数据:

        3.约束了服务器应该以何种格式来反馈数据给浏览器;

        4约束了浏览器应该以何种格式来接收服务器反馈的数据.

    2.HTTP格式规范

        1.HTTP1.0规范:

            若请求的有N个资源,得建立N次连接,发送N次请求,接收N次响应,关闭N次连接.

            每次请求的之间都要建立单独的连接,请求,响应,响应完关闭该次连接:

            缺点:每请求一个资源都要单独的建立新的连接,请求完并关闭连接.

        1.HTTP1.1规范:

            能在一次连接之间,多次请求,多次响应,响应完之后再关闭连接.

            在一个TCP连接上可以传送多个HTTP请求和响应

            多个请求和响应过程可以重叠进行

            增加了更多的请求头和响应头

        3.HTTP协议的版本

            HTTP/1.0HTTP/1.1HTTPS2.0

    请求信息

    1.一个完整的由客户端发给服务器的HTTP请求中包含以下内容:

        *请求行

            包含了请求方法、请求资源路径、HTTP协议版本

            Get/resources/imges/1.jpg/ HTTP/1.1

        *多个请求头

            包含了对客户端的环境描述、客户端请求的主机地址等信息

                1.Accept:浏览器可接受的MIME类型(Tomcat安装目录/conf/web.xml中查找)注意: MIME: 表示文件内容的类型.

                2.Accept-Charset:告知服务器,客户端支持哪种字符集

                3.Accept-Encoding:浏览器能够进行解码的数据编码方式

                4.Accept-Language:浏览器支持的语言。

                5.Referer:当前页面由哪个页面访问过来的。

                6.Content-Type:通知服务器,请求正文的MIME类型。

                7.Content-Length:请求正文的长度

                8.If-Modified-Since:通知服务器,缓存的文件的最后修改时间。

                9.User-Agent:通知服务器,浏览器类型.

                10.Connection:表示是否需要持久连接。如果服务器看到这里的值为“Keep -Alive”,或者看到请求使用的是HTTP 1.1HTTP 1.1默认进行持久连接)

                11.Cookie:这是最重要的请求头信息之一(会话有关)

    2.请求实体

        服务器返回给客户端的具体数据,比如文件数据.

        ![](https://img2020.cnblogs.com/blog/1668748/202004/1668748-20200425215713826-779651544.png)

    常见状态响应码

        状态码                     中文描述

        1xx                        服务器未完全接收客户端信息,等待一段时间,发1xx

        2xx                        成功(200)

        3xx                        重定向,对于客户端的请求,服务器将请求转发到另外一个服务器来访问(304)

        4xx                        客户端发生异常。404:(请求路径没有对应的资源),(405)请求没有对应的doxxx()方法

        5xx                        服务端发生异常

    Get个Post方法

    简单说明:在HTTP/1.1协议中,定义了8种发送http请求的方法

        GETPOSTOPTIONSHEADPUTDELETETRACECONNECTPATCH

    1.GET请求    

        1.请求的数据全部在浏览器的地址栏(很不安全)

            ttp://localhost:8080/webapp/?username=coder&email=111#

        2.请求信息全部会存储在请求行中

            注意:由于浏览器和服务器对URL长度有限制,因此在URL后面附带的参数是有限制的,通常不能超过2KB    

        3.参数连接  

              资源?参数名=参数值值&参数名=参数值&…

     

    2.POST请求

        请求的数据不会出现在浏览器的地址栏中(比较安全)

        请求信息会全部存储到请求实体中

    3.GETPOST请求的区别:

        1.GET的请求数据在地址栏中,POST不会(PostGet安全)

        2.POST请求的参数存放于请求实体中, GET存放在请求行中.

            GET请求的数据不能超过2K, POST没有上限 文件上传时,必须使用POST方式

        3.GET可以缓存, POST没有缓存

    4.如何选择GETPOST

        1.如果要传递大量数据,比如文件上传,只能用POST请求

        2.GET的安全性比POST要差些,如果包含机密敏感信息,建议用POST

        3.如果仅仅是索取数据(数据查询),建议使用GET

        4.如果是增加、修改、删除数据,建议使用POST

    HTTP无状态连接

    1.打开一个浏览器,访问某一个站点,在该网址内部查看信息,点击超链接等相关操作,最后关闭浏览器的整个过程,称之为一次会话

    2.HTTP协议

        1.它是无状态连接,服务器不知道上一次是哪个客户端请求了自己.

        2.无状态连接带来的问题:

            *在一次会话中,多个请求之间无法共享数据,无法跟踪用户的会话信息.

            *在一次会话中共享数据即会话跟踪技术.

        3.解决方案

            *使用参数的传递机制

            在每一个请求之间使用参数来传递需要共享的数据.http://localhost/param/list?username=gzy

            可以解决问题,但请求需要共享的数据全部暴露在URL(请求行),不安全.想要解决这个问题,把共享的数据存放到请求头中. 期待Cookie技术

    #-*- coding:utf-8 -*-

    @Author: A dog

    PS:如有需要Python学习资料的小伙伴可以加点击下方链接自行获取

    python免费学习资料以及群交流解答点击即可加入

    @File: Taobao.py

    @Software: PyCharm

    import datetime import time

    from selenium import webdriver

    name = ‘账号’ password = ‘密码

    指定webdriver位置

    driver = webdriver.Chrome(executable_path=‘C:chromedriver.exe’)

    打开淘宝网址

    driver.get(‘https://www.taobao.com/’)

    class pay: # 登录模块 def login_in(self, num, pwd, times): # 点击登入 driver.find_element_by_class_name(‘h’).click() time.sleep(0.3) # 发送账号密码 driver.find_element_by_id(‘fm-login-id’).send_keys(num) driver.find_element_by_id(‘fm-login-password’).send_keys(pwd) time.sleep(0.2) # 点击登入 driver.find_element_by_class_name(‘fm-btn’).click() time.sleep(1) # 进入购物车 driver.get(“https://cart.taobao.com/cart.htm”) # driver.find_element_by_id(‘mc-menu-hd’).click() # time.sleep(0.2) driver.find_element_by_id(‘J_SelectAll1’).click() time.sleep(0.5) self.auto_check1(times)

    #反复结算

    def auto_check(self,times):

    while True:

    try:

    if driver.find_element_by_id('J_SelectAll1'):

    driver.find_element_by_id('J_SelectAll1').click()

    time.sleep(0.5)

    break

    except:

    time.sleep(0.5)

    pass

    while True:

    if datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') >= times:

    while True:

    try:

    driver.find_element_by_id("J_Go").click()

    print("成功结算")

    driver.find_element_by_link_text('提交订单').click()

    print(f"抢购成功,请尽快付款")

    time.sleep(5)

    return 0

    except:

    print("无法结算,重试")

    time.sleep(1)

    driver.get("https://cart.taobao.com/cart.htm")

    self.auto_check(times)

    def auto_check1(self,times):

    while True:

    if datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f') >= times:

    while True:

    try:

    driver.find_element_by_id("J_Go").click()

    print("成功结算")

    driver.find_element_by_link_text('提交订单').click()

    print(f"抢购成功,请尽快付款")

    time.sleep(5)

    return 0

    except:

    print("无法结算,重试")

    time.sleep(1)

    driver.get("https://cart.taobao.com/cart.htm")

    self.auto_check(times)

    # 运行

    def run_driver(self, num, pwd, times):

    self.login_in(num, pwd, times)

    qq

    if name == ‘main’: jd = pay() jd.run_driver(name, password, ‘2020-12-15 19:59:59:400’)

  • 相关阅读:
    七个高效的文本编辑习惯(以Vim为例)
    rbx1 package 下载安装过程
    ros机器人开发概述
    ROS BY EXAMPLE 1 -- 环境设置与安装
    除法取模练习(51nod 1119 & 1013 )
    kinect driver install (ubuntu 14.04 & ros-indigo)
    ros问题总结
    200行代码搞定炸金花游戏(PHP版)
    JavaScript方法call,apply,caller,callee,bind的使用详解及区别
    javascript中apply、call和bind的区别
  • 原文地址:https://www.cnblogs.com/xinxihua/p/14352940.html
Copyright © 2011-2022 走看看