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

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

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

文章评论

添加新评论

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

相关文章

终于用上了专业版的PyCharm含激活方法链接[Python]

终于用上了专业版的PyCharm含激活方法链接[Python]

就直接上图片吧: 激活方法链接:https://www.cnblogs.com/pupilheart/p/9734124.html
阅读全文>>
邮件发送失败小结Deferred: Connection timed out with[Port 25]

邮件发送失败小结Deferred: Connection timed out with[Port 25]

一直在忙,尽管没有忙出什么成果,也不知道忙了啥。 周末了,终于有时间看了一下自己的小服务器,发现邮件提醒好久没有发出来了. 赶紧看了一下maillog,发现: Jul 1 15:35:02 ebs-xxx sendmail[18015]: w5UK022f017326: to=<xxx...
阅读全文>>
简易中英小词典iDict v1.0发布[Github开源]

简易中英小词典iDict v1.0发布[Github开源]

用wpf写了一个小词典,方便自己使用。 功能: 简易查词 最小化系统托盘 设置开机启动 本站下载 Github下载 Github 源码 词典是用python爬的iciba词库,基本够用了。 下面是python抓取词典源码,现学现用。python上手就是简单。 # -*- codin...
阅读全文>>
在AS/400上怎样从Journal恢复数据[DSPJRN]

在AS/400上怎样从Journal恢复数据[DSPJRN]

首先说一下什么是journal,字面翻译过来就是日志,其实很简单,就是日志记录。在as/400上所有的对文件的操作都可以通过journal记录下来,以防任何不测用以数据回滚、调查等需要。 其实在实际项目中,真正遇到需要回滚数据的时候真的是非常之少,在400上工作这么多年,基本没发生过数据回滚的...
阅读全文>>
IBM DB2 SQL to_char 函数用法总结[SQL]

IBM DB2 SQL to_char 函数用法总结[SQL]

在用sql处理数据时经常会碰到日期时间格式转换的问题,其中to_char函数有时候就会用到,特别的方便。 作为ibm提供的内置函数,功能和varchar_format差不多。 首先看一下to_char语法: TO_CHAR(time_stamp, format_string) to_char...
阅读全文>>