如何绕过登录抓取js动态加载网页数据[Python]

时间:2018-11-24 ┊ 阅读:6,970 次 ┊ 标签: 开发 , 编程 , 经验

今天经历了一翻折腾,把一个需要登录网站并js动态加载的数据一一给抓下来了。

首先,登录时有cookie,我们需要把cookie保存下来,用urllib2构建request时加入header信息,这时还多了一点,虚构了浏览器信息,让服务器以为是正常的浏览器发起的请求,这样可以绕过简单的反爬虫策略。

等用cookie搞定了登录,发现网页是js动态加载,抓取失败!
搜索了一下,发现两种方法:

  1. 用工具,构建webdriver,用chrome或firefox来打开网页,缺点效率太低。
  2. 分析网页加载过程,通过response信息找到网页加载时调用的api或服务地址,这个较烦琐麻烦,不过找到可是一劳永逸,用python全部能过后台抓取成为可能。

在几十个请求中,最后找到了后台加载数据服务地址,找到了规律,然后用id进行拼接得到完整地址,就可以构造请求了。
服务器返回的数据竟然不是json数据,而是xml,赶紧研究一下xml解析方法,我选择了minidom来解析,感觉看着舒服。

然后遇到了空标签问题,在网页上没有评论的时候,标签是空的时候,就报错了,因为直接访问list,这时会报out of index error。简单粗暴直接try然后跳过。

抓取的数据写入csv,download的附件分别保存到以id建的小文件夹下面。

时间的格式化,打印出含毫秒的长时期字符,和robot测试工具输出提示一致。

最后把中间生成的临时xml文件删除了。哈哈

WechatIMG10.png

代码:

#!/usr/bin/python
# -*- coding:utf-8 -*-

import urllib2
import xml.dom.minidom
import os
import csv
import time

def get_timestamp():
    ct = time.time()
    local_time = time.localtime(ct)
    time_head = time.strftime("%Y%m%d %H:%M:%S", local_time)
    time_secs = (ct - long(ct)) * 1000
    timestamp = "%s.%03d" % (time_head, time_secs)
    return timestamp

# create request user-agent header
userAgent = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36'
cookie = '...0000Y3Pwq2s9BdZn8e0zpTDmkVv:-1...'
uaHeaders = {'User-Agent': userAgent, 'Cookie': cookie}

# item url string connection
itemUrlHead = "https://api.amkevin.com:8888/ccm/service/com.amkevin.team.workitem.common.internal.rest.IWorkItemRestService/workItemDT88?includeHistory=false&id="
itemUrlId = "99999"
itemUrlTail = "&projectAreaItemId=_hnbI8sMnEeSExMMeBFetbg"
itemUrl = itemUrlHead + itemUrlId + itemUrlTail

# send request with user-agent header
request = urllib2.Request(itemUrl, headers=uaHeaders)
response = urllib2.urlopen(request)
xmlResult = response.read()

# prepare the xml file
curPath = os.getcwd()
curXml = curPath + '/' + itemUrlId + '.xml'
if os.path.exists(curXml):
    os.remove(curXml)
curAttObj = open(curXml, 'w')
curAttObj.write(xmlResult)
curAttObj.close()

# print xmlItem

# parse response xml file
DOMTree = xml.dom.minidom.parse(curXml)
xmlDom = DOMTree.documentElement

# prepare write to csv
csvHeader = ["ID", "Creator", "Creator UserID", "Comment Content", "Creation Date"]
csvRow = []
csvCmtOneFile = curPath + '/rtcComment.csv'
if not os.path.exists(csvCmtOneFile):
    csvObj = open(csvCmtOneFile, 'w')
    csvWriter = csv.writer(csvObj)
    csvWriter.writerow(csvHeader)
    csvObj.close()

# get comments & write to csv file
items = xmlDom.getElementsByTagName("items")
for item in items:
    try:
        if item.hasAttribute("xsi:type"):
            curItem = item.getAttribute("xsi:type")
            if curItem == "workitem.restDTO:CommentDTO":
                curCommentContent = item.getElementsByTagName("content")[0].childNodes[0].data
                curCommentContent = curCommentContent.replace('<synthetic>', '')
                curCommentContent = curCommentContent.replace('</synthetic>', '')
                curCommentCreationDate = item.getElementsByTagName("creationDate")[0].childNodes[0].data
                curCommentCreator = item.getElementsByTagName("creator")[0]
                curCommentCreatorName = curCommentCreator.getElementsByTagName("name")[0].childNodes[0].data
                curCommentCreatorId = curCommentCreator.getElementsByTagName("userId")[0].childNodes[0].data
                csvRow = []
                csvRow.append(itemUrlId)
                csvRow.append(curCommentCreatorName)
                csvRow.append(curCommentCreatorId)
                csvRow.append(curCommentContent)
                csvRow.append(curCommentCreationDate)
                csvObj = open(csvCmtOneFile, 'a')
                csvWriter = csv.writer(csvObj)
                csvWriter.writerow(csvRow)
                csvObj.close()

                # save the attachment file to local dir
                curAttFile = curPath + '/' + itemUrlId
                if not os.path.exists(curAttFile):
                    os.mkdir(curAttFile)
                curAttFile = curPath + '/' + itemUrlId + '/' + itemUrlId + '.csv'
                if os.path.exists(curAttFile):
                    curCsvObj = open(curAttFile, 'a')
                    curCsvWriter = csv.writer(curCsvObj)
                    curCsvWriter.writerow(csvRow)
                else:
                    curCsvObj = open(curAttFile, 'w')
                    curCsvWriter = csv.writer(curCsvObj)
                    curCsvWriter.writerow(csvRow)
                curCsvObj.close()
                print get_timestamp() + " :" + "  INFO :" + " write comments to csv success."
    except:
        print get_timestamp() + " :" + "  INFO :" + " parse xml encountered empty element, skipped."
        continue

# get attachment
linkDTOs = xmlDom.getElementsByTagName("linkDTOs")
for linkDTO in linkDTOs:
    try:
        if linkDTO.getElementsByTagName("target")[0].hasAttribute("xsi:type"):
            curAtt = linkDTO.getElementsByTagName("target")[0].getAttribute("xsi:type")
            if curAtt == "workitem.restDTO:AttachmentDTO":
                curAttUrl = linkDTO.getElementsByTagName("url")[0].childNodes[0].data
                curTarget = linkDTO.getElementsByTagName("target")[0]
                curAttName = curTarget.getElementsByTagName("name")[0].childNodes[0].data
                # save the attachment file to local dir
                curAttFolder = curPath + '/' + itemUrlId
                if not os.path.exists(curAttFile):
                    os.mkdir(curAttFolder)
                curAttFile = curPath + '/' + itemUrlId + '/' + curAttName
                curRequest = urllib2.Request(curAttUrl, headers=uaHeaders)
                curResponse = urllib2.urlopen(curRequest)
                curAttRes = curResponse.read()
                if os.path.exists(curAttFile):
                    os.remove(curAttFile)
                curAttObj = open(curAttFile, 'w')
                curAttObj.write(curAttRes)
                curAttObj.close()
                print get_timestamp() + " :" + "  INFO :" + " download attachment [" + curAttName + "] success."
    except:
        print get_timestamp() + " :" + "  INFO :" + " parse xml encountered empty element, skipped."
        continue

    # print linkDTO

# delete temporary xml file
if os.path.exists(curXml):
    os.remove(curXml)

文章评论

添加新评论

温馨提醒:如果您是第一次在本站留言,需要审核后才能显示哦!

相关文章

Mac电脑一直显示有未读信息怎么解决?

Mac电脑一直显示有未读信息怎么解决?

上次还是macOS14,一直没有解决。索性放着不管了,反正把所有没用的信息都清理了,已经确认没有未读信息了。 最近升级了macOS15最新版本,但还是提示有6条未读信息,百思不得其姐! 查了一下网上不少人遇到此问题,有说有bug,有说点掉过滤未知联系人信息啥,都不好用。 最后查到了Apple社...
阅读全文>>
Whatsapp只能查看最近三天的聊天记录了怎么办?

Whatsapp只能查看最近三天的聊天记录了怎么办?

今天突然发现电脑上的WhatsApp只能查看最近3天的聊天记录了,显示要查看3天前的记录请到手机上查看。很是吃惊,以前一直是所有记录都自动同步到电脑版的啊,不论换电脑了还是换手机了,聊天记录从来没丢过。 查询一翻也没有找出来原因,怎么也同步不过去。 电脑版app重新安装了,也不行。 有个方法说...
阅读全文>>
ValueError: Error getting directory

ValueError: Error getting directory

ssl证书过期了 明明自动renew的 然后看log已经好久没更新成功了 查半天是当前server不信任远程连接 只能自己改代码 报错: 21-12-17 13:01:11 Generate CSR...amkevin.csr amkevin.csr generated. /home/www...
阅读全文>>
网页路径中的'.'和'..'还有'./'和'../'区别

网页路径中的'.'和'..'还有'./'和'../'区别

. 表示当前目录 .. 表示当前目录的上一级目录。 ./表示当前目录下的某个文件或文件夹,视后面跟着的名字而定 ../表示当前目录上一级目录的文件或文件夹,视后面跟着的名字而定。 例如: 文件夹 a 下面有 文件夹b c 和文件 d。 文件夹b下面有e.php 和文件f。 则e中的 . 表示...
阅读全文>>
Introduction to ILE RPG Activation Groups

Introduction to ILE RPG Activation Groups

Learn how activation groups can help your ILE RPG programs run more efficiently, how to specify the type of group to use, and closing and reclaimin...
阅读全文>>