使用 PyQT 抓取工具下载 "disguised" 个文件
Download "disguised" file using a PyQT scraper
嗯你好
我很难使用 PyQT 抓取网站。问题是,该站点使用 JSF,并且 URL 对于其中的所有内容都是相同的。让我们在这个例子中使用“http://somesi.te/searchData.jsf”。
我打开它,填写一个表格(目标是相同的URL),提交,并在同一页面中得到结果。直到那里我的脚本工作正常。
但结果 return 像 1000 个分页页面,底部有一个锚标记用于下载一个 Excel 文件,所有结果都在一个文件中。
这个锚是#-targeted,onClick 事件调用 MoJarra 函数,jsfcljs:
<a href="#" onclick="mojarra.jsfcljs(document.getElementById('formX'),{'formX:j_idt999':'formX:j_idt999'},'');return false">
分析函数,就是简单的在表单里追加了一个<input type="hidden">
,然后重新提交,估计是为了让服务器看懂,发给我的是Excel文件,而不是网页。
问题是:服务器return将文件"disguised"作为网页。 URL 仍然是 "somesi.te/searchData.jsf",并且由于 headers,浏览器将其识别为文件。
当我尝试使用 unsupportedContent() 获取文件时,它似乎没有跟随流,它只是尝试获取在 unsupportedContent 信号中收到的 URL。作为当前 URL "somesi.te/searchData.jsf",当我保存文件时,我得到的是页面的源代码,而不是我的文件。
这是我现在使用的代码:
def getTicketDetails(self):
self.webView.loadFinished.disconnect(self.getTicketDetails)
self.webView.page().setForwardUnsupportedContent(True)
self.webView.page().unsupportedContent.connect(self.downloadExcel)
downloadButton = documentElement.findFirst('input[id="force_download"]')
downloadButton.evaluateJavaScript("this.click();")
def downloadExcel(self, reply):
self.manager = self.webView.page().networkAccessManager()
self.manager.finished.connect(self.saveExcel)
postData = QByteArray()
postData.append("formX=formX&")
postData.append("formX:idSelecao_input=1&")
postData.append("formX:dataJanela_input=03/11/2015&")
postData.append("formX:dataAte_input=07/11/2015&")
postData.append("formX:j_idt106_input=all&")
postData.append("formX:j_idt120_input=0&")
postData.append("formX:produto_input=VLR&")
postData.append("formX:options=1&")
postData.append("formX:j_idt133_input=0&")
#postData.append("javax.faces.ViewState=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&")
postData.append("formX:j_idt999=formCI:j_idt999")
self.request = reply.request()
#self.reply = self.manager.get(self.request)
self.reply = self.manager.post(reply.request(), postData)
def saveExcel(self):
f = open('C:\path\to\file\searchResults.xls', 'wb')
f.write(str(self.reply.readAll()))
f.flush()
f.close()
self.exit()
这里故意更改了文件路径。
我尝试使用 post 方法以及 get 方法(在上面的代码段中进行了评论),但是其中 none 有效,我总是以损坏的 xls 文件结束确实是一个包含页面源代码的 HTML 文件。
当我取消注释 post 数据项 "javax.faces.ViewState" 时,服务器 return 出错。我猜这里是缺少的关键,因为这一特定行上的特殊字符。
你知道我做错了什么吗?我知道我用 python 的方式,但我根本不是专家。
如果需要澄清,请告诉我。
我目前正在使用 python 2.7.9 和 PyQT 4.8.7。
在这里,我创建了这段代码:
def specialCharsToHtmlHex(self, rawString, jsfViewState=False):
parsedString = (rawString.replace('+', '%2B')
.replace('/', '%2F')
.replace(':', '%3A')
.replace('?', '%3F'))
if jsfViewState:
parsedString = parsedString.replace('=', '%3D')
return parsedString
我对 ViewState 进行了这种特殊处理,因为我无法将“=”转换为 post 结构的一部分。
然后我这样调用代码:
postData = QByteArray()
postData.append(self.specialCharsToHtmlHex("formX=formX&"))
postData.append(self.specialCharsToHtmlHex("formX:idSelecao_input=1&"))
postData.append(self.specialCharsToHtmlHex("formX:dataJanela_input="+self.searchData['startDate']+"&"))
postData.append(self.specialCharsToHtmlHex("formX:dataAte_input="+self.searchData['endDate']+"&"))
postData.append(self.specialCharsToHtmlHex(self.fieldNames['horaJanela']+"=&"))
postData.append(self.specialCharsToHtmlHex(self.fieldNames['tipoPortabilidade']+"=&"))
postData.append(self.specialCharsToHtmlHex(self.fieldNames['statusBilhete']+"="+self.searchData['statusValue']+"&"))
postData.append(self.specialCharsToHtmlHex(self.fieldNames['operadora']+"=0&"))
postData.append(self.specialCharsToHtmlHex("formX:produto_input="+self.searchData['productValue']+"&"))
postData.append(self.specialCharsToHtmlHex("formX:options=1&"))
postData.append(self.specialCharsToHtmlHex("formX:cpf=&"))
postData.append(self.specialCharsToHtmlHex(self.fieldNames['janelaBloqueada']+"=0&"))
postData.append(self.specialCharsToHtmlHex("formX:dataTable_selection=&"))
postData.append(self.specialCharsToHtmlHex("javax.faces.ViewState="+self.specialCharsToHtmlHex(self.searchData['jsfViewState'], jsfViewState=True)+"&"))
postData.append(self.specialCharsToHtmlHex(self.fieldNames['mojarraHiddenInput']+"="+self.searchData['mojarraHiddenValue']))
self.reply = self.webView.page().networkAccessManager().post(reply.request(), postData)
然后问题就解决了,这样我就得到了我想要的 Excel 文件。
我知道有一些冗余,但为了保护,我决定保持这样。
嗯你好
我很难使用 PyQT 抓取网站。问题是,该站点使用 JSF,并且 URL 对于其中的所有内容都是相同的。让我们在这个例子中使用“http://somesi.te/searchData.jsf”。
我打开它,填写一个表格(目标是相同的URL),提交,并在同一页面中得到结果。直到那里我的脚本工作正常。
但结果 return 像 1000 个分页页面,底部有一个锚标记用于下载一个 Excel 文件,所有结果都在一个文件中。 这个锚是#-targeted,onClick 事件调用 MoJarra 函数,jsfcljs:
<a href="#" onclick="mojarra.jsfcljs(document.getElementById('formX'),{'formX:j_idt999':'formX:j_idt999'},'');return false">
分析函数,就是简单的在表单里追加了一个<input type="hidden">
,然后重新提交,估计是为了让服务器看懂,发给我的是Excel文件,而不是网页。
问题是:服务器return将文件"disguised"作为网页。 URL 仍然是 "somesi.te/searchData.jsf",并且由于 headers,浏览器将其识别为文件。
当我尝试使用 unsupportedContent() 获取文件时,它似乎没有跟随流,它只是尝试获取在 unsupportedContent 信号中收到的 URL。作为当前 URL "somesi.te/searchData.jsf",当我保存文件时,我得到的是页面的源代码,而不是我的文件。
这是我现在使用的代码:
def getTicketDetails(self):
self.webView.loadFinished.disconnect(self.getTicketDetails)
self.webView.page().setForwardUnsupportedContent(True)
self.webView.page().unsupportedContent.connect(self.downloadExcel)
downloadButton = documentElement.findFirst('input[id="force_download"]')
downloadButton.evaluateJavaScript("this.click();")
def downloadExcel(self, reply):
self.manager = self.webView.page().networkAccessManager()
self.manager.finished.connect(self.saveExcel)
postData = QByteArray()
postData.append("formX=formX&")
postData.append("formX:idSelecao_input=1&")
postData.append("formX:dataJanela_input=03/11/2015&")
postData.append("formX:dataAte_input=07/11/2015&")
postData.append("formX:j_idt106_input=all&")
postData.append("formX:j_idt120_input=0&")
postData.append("formX:produto_input=VLR&")
postData.append("formX:options=1&")
postData.append("formX:j_idt133_input=0&")
#postData.append("javax.faces.ViewState=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&")
postData.append("formX:j_idt999=formCI:j_idt999")
self.request = reply.request()
#self.reply = self.manager.get(self.request)
self.reply = self.manager.post(reply.request(), postData)
def saveExcel(self):
f = open('C:\path\to\file\searchResults.xls', 'wb')
f.write(str(self.reply.readAll()))
f.flush()
f.close()
self.exit()
这里故意更改了文件路径。
我尝试使用 post 方法以及 get 方法(在上面的代码段中进行了评论),但是其中 none 有效,我总是以损坏的 xls 文件结束确实是一个包含页面源代码的 HTML 文件。
当我取消注释 post 数据项 "javax.faces.ViewState" 时,服务器 return 出错。我猜这里是缺少的关键,因为这一特定行上的特殊字符。
你知道我做错了什么吗?我知道我用 python 的方式,但我根本不是专家。
如果需要澄清,请告诉我。
我目前正在使用 python 2.7.9 和 PyQT 4.8.7。
在这里,我创建了这段代码:
def specialCharsToHtmlHex(self, rawString, jsfViewState=False):
parsedString = (rawString.replace('+', '%2B')
.replace('/', '%2F')
.replace(':', '%3A')
.replace('?', '%3F'))
if jsfViewState:
parsedString = parsedString.replace('=', '%3D')
return parsedString
我对 ViewState 进行了这种特殊处理,因为我无法将“=”转换为 post 结构的一部分。
然后我这样调用代码:
postData = QByteArray()
postData.append(self.specialCharsToHtmlHex("formX=formX&"))
postData.append(self.specialCharsToHtmlHex("formX:idSelecao_input=1&"))
postData.append(self.specialCharsToHtmlHex("formX:dataJanela_input="+self.searchData['startDate']+"&"))
postData.append(self.specialCharsToHtmlHex("formX:dataAte_input="+self.searchData['endDate']+"&"))
postData.append(self.specialCharsToHtmlHex(self.fieldNames['horaJanela']+"=&"))
postData.append(self.specialCharsToHtmlHex(self.fieldNames['tipoPortabilidade']+"=&"))
postData.append(self.specialCharsToHtmlHex(self.fieldNames['statusBilhete']+"="+self.searchData['statusValue']+"&"))
postData.append(self.specialCharsToHtmlHex(self.fieldNames['operadora']+"=0&"))
postData.append(self.specialCharsToHtmlHex("formX:produto_input="+self.searchData['productValue']+"&"))
postData.append(self.specialCharsToHtmlHex("formX:options=1&"))
postData.append(self.specialCharsToHtmlHex("formX:cpf=&"))
postData.append(self.specialCharsToHtmlHex(self.fieldNames['janelaBloqueada']+"=0&"))
postData.append(self.specialCharsToHtmlHex("formX:dataTable_selection=&"))
postData.append(self.specialCharsToHtmlHex("javax.faces.ViewState="+self.specialCharsToHtmlHex(self.searchData['jsfViewState'], jsfViewState=True)+"&"))
postData.append(self.specialCharsToHtmlHex(self.fieldNames['mojarraHiddenInput']+"="+self.searchData['mojarraHiddenValue']))
self.reply = self.webView.page().networkAccessManager().post(reply.request(), postData)
然后问题就解决了,这样我就得到了我想要的 Excel 文件。
我知道有一些冗余,但为了保护,我决定保持这样。