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

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

今天经历了一翻折腾,把一个需要登录网站并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)

文章评论

添加新评论

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

相关文章

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...
阅读全文>>
popup.js怎么和content.js通信?[JQuery]

popup.js怎么和content.js通信?[JQuery]

这两天为了实现一个谷歌浏览器插件功能,研究了半天怎么让插件来改特定网页里的特定字段的值,而这个值又来自popup的网页预先设定,下一步实现动态加载,可以让更多组实现便利。 目的很简单,我们有一个list,需要填到网页的某个字段,当然是好几个,这些值是设定好的,网页系统我们没法改,input想...
阅读全文>>
T480自动关机无法启动(解决办法)

T480自动关机无法启动(解决办法)

周五升级更新后,用一会就关机,还开不了,怎么按开机键也没有反应。 周六折腾一上午,拔电源,断电,20秒复位,把电池都拿下来了。都不行。 下午实在没办法了,给售后打电话让拿去检测一下。 然后拿售后去了,给检测一会没问题,说内存不干净给擦一下好了,开机正常了。 付款50块检测费,开心的拿回家,一...
阅读全文>>
office2016 产品已激活 却同时显示需要激活(解决办法)

office2016 产品已激活 却同时显示需要激活(解决办法)

直接上命令吧: c:\Program Files\Microsoft Office\Office16>cscript.exe ospp.vbs /dstatus Microsoft (R) Windows Script Host Version 5.812 版权所有(C) Microso...
阅读全文>>
Excel 2016打开xls文件显示空白的解决办法

Excel 2016打开xls文件显示空白的解决办法

问题描述: 安装Office 2016后,正版激活一直挺好的,不过今天发现,双击EXCEL旧版本文件打开后显示为空白。 上网搜索一翻,终于知道问题所在,可能和昨天的一个office更新有关。 解决办法: 开始——运行——regedit 修改HKEY_CLASSES_ROOT\Excel.She...
阅读全文>>