我有两个URL。
它不会返回任何表。我的目标是最终计算站点上产品类别的数量。
我了解问题在于html的结构。我用pythons pandas尝试了不同的解决方案,但没有任何帮助。
可以帮忙吗?
我希望的只是利用熊猫来获取数据。
我尝试过
import requests
URL = 'http://semleragro.dk/brugte-maskiner/brugte-maskiner/'
content = requests.get(URL)
soup = BeautifulSoup(content.text, 'html.parser')
row = soup.find('tr') # Extract and return first occurrence of tr
print(row) # Print row with HTML formatting ```
I tried a simple
将熊猫作为pd导入
wiki_timeline = pd.read_html('http://semleragro.dk/brugte-maskiner/brugte-maskiner/')
What I actually needed, was to be able to import the data into excel. But sorry for concerns about the links!
答案 0 :(得分:0)
访问第二个链接时得到404。
对于第一个链接,如果您看一下html,则任何地方都没有<table>
,<tr>
或<td>
HTML标记。这是因为大部分页面都包裹在<iframe>
中,而源页面是https://dealers.mascus.com/semleragro/search.aspx。
如果您将其抓取,将会有更多的运气:
def main():
from bs4 import BeautifulSoup
import requests
url = "https://dealers.mascus.com/semleragro/search.aspx"
page = requests.get(url)
soup = BeautifulSoup(page.content, "html.parser")
table = soup.find("table", {"class": "list list_vertical"})
for row in table.find_all("tr", {"class": "item"}):
cell_2 = row.find("td", {"class": "cell2 cell_brandmodel"})
title = cell_2.find("a").text
print(title)
return 0
if __name__ == "__main__":
import sys
sys.exit(main())
输出:
Amazone UG3000
Amazone UG3000
Hardi 2800 L 20 M TRAILER
Hardi TZ-TX 2400
John Deere
John Deere
John Deere 732 20M
John Deere 740 24M
John Deere 740I TRAILERSPRØJTE
John Deere 832I
John Deere 832I
John Deere M740 TRAILERSPRØJTE
Kuhn 5600I MARKSPRØJTE
Kuhn METRIS 4102 24M
Case IH MXM 190
Case IH PUMA 230 M/FRONTLIFT
Case IH PUMA CVX230
CLAAS AXION 850
CLAAS XERION 4000
Deutz 615S
您甚至不需要熊猫。只是BeautifulSoup和请求。
编辑-我看过以下链接:
https://www.kragmann.dk/da/brugte-maskiner/entreprenor.html/#/
我承认,找到这个的“来源”并不是那么简单。
我要做的第一件事是查看页面完全呈现后浏览器(Chrome)看到的内容。如果您使用的是Chrome浏览器,则可以通过按F12并查看“元素”标签来执行此操作-然后根据需要展开和折叠HTML标签树视图。大多数现代浏览器都支持这种功能。无论如何,在该树视图中看到的内容不一定与在原始HTML源代码中看到的内容相同,因为该视图由所有HTML元素组成,包括由JavaScript动态生成的元素或通过其他方式。这是我在树状视图中看到的内容:
单个“广告”(待售车辆)在“外部包装” div下,而在“二手机械” div下。 div被称为“外部包装器”这一事实表明它的内容是由某些脚本生成的。
然后,我查看了页面的实际源代码(在Chrome中,右键单击->“查看页面源代码”)-看起来与我们在树状视图中看到的内容不同:
我可以看到“ usedMachines” div,但“ external-wrapper” div不存在。据怀疑,它是使用JavaScript和JQuery生成的。通常,这是我建议您使用Selenium的地方,但我会更深入地研究。
在“ usedMachines” div上方,有一个<script>
标签,看起来像这样:
<script src="https://lister.maskinbladet.dk/system/data/loader3.js" type="text/javascript"></script>
在最上方查看该JavaScript文件的源代码,您会看到一些有趣的变量,例如:
basePath = 'https://lister.maskinbladet.dk/system/data/'
访问该URL给我403错误。该页面告诉我,由于我未提交正确的凭据,因此我的访问被拒绝。幸运的是,我们可以在JavaScript中看到它期望看到的一些有效负载键(GET参数):
defaultConfig = {
type: 'normal',
count: '1',
callbackURL: '',
fncallback: '',
hashtype: 'normal',
showall: false,
fbshare: false
...
我想抓取这个basePath网址。所以,现在我有了一个URL,而且我知道我必须欺骗我的凭据和GET参数。往下看,我看到了:
ajaxUrl = basePath+'jsonp3.php',
很高兴知道。这是我们要抓取的实际网址。
然后,代码继续使用GET参数填充ident
字典/映射(我猜是“ identity”的缩写)。您可以尝试对整个过程进行逆向工程以获得GET参数的最终列表,但是只需从Chrome的资源日志中获取它们就容易得多,我将在稍后进行介绍。
我们几乎已经看完JavaScript了-以下是另一个有趣的代码段:我很好奇看到ajaxUrl
最终在哪里使用了-即在此函数中:
function JSONajax(attr, callBack) {
jQuery.ajax({
url: ajaxUrl, type: 'GET', data: attr, dataType: 'jsonp'
})
.done(function(data){
if (typeof(callBack) === 'function') callBack.call(this, data);
});
}
该函数似乎接受两个参数attr
和callback
。然后,该函数向我们的ajaxUrl
发出GET请求,并将attr
与请求一起传递。这告诉我第一个参数必须是某种有效负载或凭据键值对的集合。然后,它将第二个参数(它是一个函数)绑定为要在收到响应时触发的回调。如果您在JavaScript中搜索“ JSONajax”,则可以确认将ident
字典/映射作为第一个参数传入,这更能证明ajaxUrl
是我们的网址想刮。
现在,我只需要欺骗我的凭据和GET参数。在Chrome中,点击F12,然后打开“网络”标签。然后,我开始记录网络流量并刷新页面。我寻找了“ jsonp3.php”,日志显示了此请求-响应历史记录:
在请求URL中显示所有GET参数。再往下一点,我们还获得了凭证和GET参数的更漂亮视图。多么方便!
剩下要做的就是编写一个Python脚本来进行实际的抓取,如下所示:
def main():
from bs4 import BeautifulSoup
import requests
base_path = "https://lister.maskinbladet.dk/system/data/"
ajax_url = base_path + "jsonp3.php"
headers = {
"Referer": "https://www.kragmann.dk/da/brugte-maskiner/entreprenor.html/",
"Sec-Fetch-Mode": "no-cors",
"User-Agent": "Mozilla/5.0"
}
params = {
"callback": "jQuery112409740978028988687_1570979614706",
"locationGUID": "E3EBC2C4-A913-4B75-8918-3BFC7851EF31",
"version": "adlist",
"count": "1",
"showall": "true",
"fbshare": "false",
"showyh": "false",
"pager": "false",
"contact": "false",
"noPrice": "false",
"prefix": "true",
"orderbyBrand": "false",
"languageGUID": "63927773-3f10-4874-b08f-823e9aa456ee",
"currencyGUID": "51597510-a206-4353-ae21-ce8d648fe948",
"treeGUID": "f268d600-625f-4650-ab91-b3cfef4c9f0d",
"_": "1570979614707"
}
response = requests.get(ajax_url, headers=headers, params=params)
assert response.status_code == 200
# Unfortunately, the response isn't raw HTML - there is HTML but it's encapsulated by JQuery stuff...
text = response.text[response.text.index("<div"):-4].replace("\\", "")
soup = BeautifulSoup(text, "html.parser")
for ad in soup.find_all("div", {"class": "ad-detail"}):
attributes = ["brand", "model", "price"]
infos = []
for attribute in attributes:
try:
info = ad.find("div", {"class": f"ad-{attribute}"}).text
assert info
infos.append(info)
except (AttributeError, AssertionError):
infos.append("N/A")
for attribute, info in zip(attributes, infos):
print(f"{attribute}: {info}")
print()
return 0
if __name__ == "__main__":
import sys
sys.exit(main())
我在这里做过一些骇人听闻的事情-因此有很大的改进空间。输出:
brand: Hyundai
model: R16-9
price: 158.000,- DKK
brand: JCB
model: 8050RTS
price: 250.000,- DKK
brand: New Holland
model: E18C
price: 155.000,- DKK
brand: New Holland
model: E60C
price: 576.900,- DKK
brand: Takeuchi
model: TB230
price: 270.000,- DKK
brand: Takeuchi
model: TB230
price: 285.000,- DKK
brand: Volvo
model: ECR25D
price: 175.000,- DKK
brand: New Holland
model: L125
price: N/A
brand: - - -
model: Perkins 1104 Motor
price: 38.000,- DKK
brand: JLG
model: 4013
price: N/A
brand: New Holland
model: LM6.28
price: N/A
brand: New Holland
model: B115C TSS
price: 889.300,- DKK
brand: New Holland
model: BRUGT MOTOR NH 95 / LB
price: N/A
brand: - - -
model: DIVERSE SKOVLE
price: N/A
brand: JST
model: KIPBAR PLANERSKOVL
price: 14.000,- DKK
brand: JST
model: N/A
price: N/A