ExcelHome技术论坛

 找回密码
 免费注册

QQ登录

只需一步,快速开始

快捷登录

搜索
EH技术汇-专业的职场技能充电站 妙哉!函数段子手趣味讲函数 Excel服务器-会Excel,做管理系统 Excel Home精品图文教程库
HR薪酬管理数字化实战 Excel 2021函数公式学习大典 Excel数据透视表实战秘技 打造核心竞争力的职场宝典
300集Office 2010微视频教程 数据工作者的案头书 免费直播课集锦 ExcelHome出品 - VBA代码宝免费下载
用ChatGPT与VBA一键搞定Excel WPS表格从入门到精通 Excel VBA经典代码实践指南
查看: 12571|回复: 12

[分享] 给大家介绍一种简单高效且易于维护的的VBA爬虫方法

[复制链接]

TA的精华主题

TA的得分主题

发表于 2019-3-19 00:04 | 显示全部楼层 |阅读模式
大家好,我是杨明胜。今天给大家介绍一个VBA爬虫的新方法。在介绍新方法的同时,我们和老方法做一个对比。我们以获取百度电影排行榜为例。网址如下:
http://top.baidu.com/buzz/movie.html
用来方法的实现方式如下:
Sub GetBaiduMovieTop50ViaOldMethod()
    Dim resp As String, i As Long, arr() As String, reg As Object
    Dim Matches As Object, match As Object, str As String
    Set reg = CreateObject("vbscript.regexp")
    reg.Pattern = "list-title.*?(</a>)"   '这个正则表达式需翻源码找规律
    reg.Global = True
    resp = GetWebTxt("http://top.baidu.com/buzz/movie.html")
    Set Matches = reg.Execute(resp)
    For Each match In Matches
        str = Right(match, Len(match) - InStr(match, """>") - 1)
        str = Left(str, InStr(str, "<") - 1)
        ReDim Preserve arr(i)
        arr(i) = str
        i = i + 1
    Next
    ActiveSheet.Range("A1:A" & 1 + UBound(arr())) = WorksheetFunction.Transpose(arr())
End Sub


'获取网页源码
Public Function GetWebTxt(url As String) As String
    Dim xmlHttp As Object
    Application.DisplayAlerts = False
    Set xmlHttp = CreateObject("MSXML2.XMLHTTP")
    xmlHttp.Open "GET", url, False
    xmlHttp.Send
    While xmlHttp.ReadyState <> 4
        DoEvents
    Wend
    GetWebTxt = BytesToBstr(xmlHttp.responsebody)
End Function

'将字节转换为字符串
Private Function BytesToBstr(Bytes)
    Dim Unicode As String
    If Len(encode) > 0 Then
        Unicode = encode
    Else
        If IsUTF8(Bytes) Then '如果不是UTF-8编码则按照GB2312来处理
            Unicode = "UTF-8"
        Else
            Unicode = "GB2312"
        End If
    End If

    Dim objstream As Object
    Set objstream = CreateObject("ADODB.Stream")
    With objstream
        .Type = 1
        .Mode = 3
        .Open
        .Write Bytes
        .Position = 0
        .Type = 2
        .Charset = Unicode
        BytesToBstr = .ReadText
       .Close
    End With
End Function

'判断网页编码函数
Private Function IsUTF8(Bytes) As Boolean
    Dim i As Long, AscN As Long, Length As Long
    Length = UBound(Bytes) + 1

    If Length < 3 Then
        IsUTF8 = False
        Exit Function
    ElseIf Bytes(0) = &HEF And Bytes(1) = &HBB And Bytes(2) = &HBF Then
        IsUTF8 = True
        Exit Function
    End If

    Do While i <= Length - 1
        If Bytes(i) < 128 Then
            i = i + 1
            AscN = AscN + 1
        ElseIf (Bytes(i) And &HE0) = &HC0 And (Bytes(i + 1) And &HC0) = &H80 Then
            i = i + 2

        ElseIf i + 2 < Length Then
            If (Bytes(i) And &HF0) = &HE0 And (Bytes(i + 1) And &HC0) = &H80 And (Bytes(i + 2) And &HC0) = &H80 Then
                 i = i + 3
            Else
                IsUTF8 = False
                Exit Function
            End If
        Else
            IsUTF8 = False
            Exit Function
        End If
    Loop

    If AscN = Length Then
        IsUTF8 = False
    Else
        IsUTF8 = True
    End If
End Function

大家可以看到用旧方法需要获取并分析网页源码,用正则匹配源码后还需用字符串处理函数获得电影名称。而用新方法无需查看网页源码,代码量也会少很多,只需以下一段代码即可:
Sub GetBaiduMovieTop50ViaNewMethod()
    Dim i As Long, arr(49) As String, doc As Object, nodebefore As String, nodeafter As String
    nodebefore = "/html[1]/body[1]/div[1]/div[3]/div[2]/div[1]/table[1]/tr["
    nodeafter = "]/td[2]/a[1]"
    Set doc = s.getdoc("http://top.baidu.com/buzz/movie.html", "gbk")  '获取网页文档
    arr(0) = s.getnode(doc, nodebefore & 2 & nodeafter) '前四个电影比较特殊
    arr(1) = s.getnode(doc, nodebefore & 4 & nodeafter)
    arr(2) = s.getnode(doc, nodebefore & 6 & nodeafter)
    arr(3) = s.getnode(doc, nodebefore & 8 & nodeafter)
    For i = 5 To 50
        arr(i - 1) = s.getnode(doc, nodebefore & 4 + i & nodeafter)
    Next i
    ActiveSheet.Range("A1:A" & 1 + UBound(arr())) = WorksheetFunction.Transpose(arr())
End Sub

使用新方法获取网页文档后,只需从文档中根据标签路径取出需要的标签即可。那么问题来了,我们该怎样知道标签路径呢?其实方法很简单,只需在立即窗口(Ctrl+G)执行以下语句即可
?s.shownodes("http://top.baidu.com/buzz/movie.html","gbk")
通过以上语句可以将网页中的所有标签解析到工作表中,如下:
aaa.png
根据标签与内容的对应关系需要什么标签就取什么标签即可。
需要注意的是新方法需要引用一个插件sqlcelfuncs,该插件免费且只有三四兆,截至目前有90多个函数,本例爬虫只用了其中3个。sqlcelfuncs下载地址如下:
https://sqlcel.com/sqlcelfuncs/

评分

2

查看全部评分

TA的精华主题

TA的得分主题

发表于 2019-3-20 01:39 来自手机 | 显示全部楼层
用htmlfile解析网页足矣,不需要用外挂的加载项,因为没必要.

TA的精华主题

TA的得分主题

 楼主| 发表于 2019-3-20 09:23 | 显示全部楼层
[广告] VBA代码宝 - VBA编程加强工具 · VBA代码随查随用  · 内置多项VBA编程加强工具       ★ 免费下载 ★      ★使用手册
htmlfile解析效率非常低,要翻源码,经常用for循环找标签,定位标签位置困难。
用sqlcelfuncs可以在5秒内定位到任何一个标签。
如果网页标签有变动,htmlfile维护起来特别麻烦,而sqlcelfuncs只需要再次定位一下标签即可。

TA的精华主题

TA的得分主题

 楼主| 发表于 2019-3-20 09:26 | 显示全部楼层
sqlcelfuncs最强的是数据集函数。数据集函数可在内存中对数据计算、筛选、修改和读写

TA的精华主题

TA的得分主题

发表于 2019-11-14 22:18 | 显示全部楼层

TA的精华主题

TA的得分主题

发表于 2019-11-15 09:07 | 显示全部楼层
这个确实厉害啊 ,谢谢楼主分享

TA的精华主题

TA的得分主题

发表于 2019-11-15 09:24 来自手机 | 显示全部楼层

TA的精华主题

TA的得分主题

发表于 2020-7-19 13:32 | 显示全部楼层

TA的精华主题

TA的得分主题

 楼主| 发表于 2020-7-19 16:17 | 显示全部楼层
[广告] Excel易用宝 - 提升Excel的操作效率 · Excel / WPS表格插件       ★免费下载 ★       ★ 使用帮助
相对论R 发表于 2020-7-19 13:32
为啥我的提示doc=nothing?

1、要运行过去。
2、这个网址标签可能过期了

TA的精华主题

TA的得分主题

发表于 2020-7-19 16:40 | 显示全部楼层
Askinofblue 发表于 2020-7-19 16:17
1、要运行过去。
2、这个网址标签可能过期了

有个小问题向请教一下:
//*[@id="container"]/div[2]/div[1]/div[3]/div[1]/div/a[2]/span
这是我从一个网站复制出来的Xpath,这东西该怎么应用呢?然后你的代码中nodebefore与nodeafter又是从哪里来的呀?arr(0)写成这样是什么原理呢?
谢谢您的解答~
您需要登录后才可以回帖 登录 | 免费注册

本版积分规则

手机版|关于我们|联系我们|ExcelHome

GMT+8, 2024-4-17 04:36 , Processed in 0.045577 second(s), 11 queries , Gzip On, MemCache On.

Powered by Discuz! X3.4

© 1999-2023 Wooffice Inc.

沪公网安备 31011702000001号 沪ICP备11019229号-2

本论坛言论纯属发表者个人意见,任何违反国家相关法律的言论,本站将协助国家相关部门追究发言者责任!     本站特聘法律顾问:李志群律师

快速回复 返回顶部 返回列表