如何解决Jsoup属性选择器返回空
String url = "https://www.google.com/search?site=imghp&tbm=isch&source=hp&q=audi&gws_rd=cr";
org.jsoup.nodes.Document doc = Jsoup.connect(url).get();
Elements elements = doc.select("div.isv-r.PNCib.MSM1fd.BUooTd");
ImageData是使用base64编码的,因此为了获得实际的图像URL,我首先要获取设置为属性的数据ID,这有效
for (Element element : elements) {
String id = element.attr("data-id")).get();
我需要与url+"#imgrc="+id
建立新连接,
org.jsoup.nodes.Document imgdoc = Jsoup.connect(url+"#"+id).get();
现在,当我检查所需数据是否位于<div jsname="CGzTgf">
中时,已经在浏览器中了,所以我在Jsoup中也做同样的事情
Elements images = imgdoc.select("div[jsname='CGzTgf']");
//futher steps
但是图像总是返回空,我找不到错误,我在android的新线程内执行此操作,任何帮助将不胜感激
解决方法
找出您的操作方式,您将完全在错误的地方寻找。网址包含在响应中包含的一些javascript <script>
标签中。
我已经从相关的<script>
标签(其中一个包含属性nonce
的标签)中提取和过滤。
然后,我将这些标签过滤为一个标签,其中包含一个使用的特定函数名和一个我期望找到的通用搜索字符串(其他<script>
标签中不会包含该标签)。
接下来,需要剥离获得的值以获取包含约十万个数组的JSON对象。然后,我(手动)对此进行导航,以拉出包含相关URL节点的节点子集。然后,我再次对其进行过滤,以获取List<String>
来获取完整的URL。
最后,我重用了以前的解决方案中的一些代码:https://stackoverflow.com/a/63135249/7619034,类似于下载图像。
然后,您还将获得一些控制台输出,详细说明哪个URL以哪个文件ID结尾。无论实际格式如何,文件都标有image_[x].jpg
标签(因此,您可能需要对其稍作修改-提示:如果提供,请从url中获取文件扩展名)。
import com.jayway.jsonpath.JsonPath;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.List;
public class GoogleImageDownloader {
private static int TIMEOUT = 30000;
private static final int BUFFER_SIZE = 4096;
public static final String RELEVANT_JSON_START = "AF_initDataCallback(";
public static final String PARTIAL_GENERIC_SEARCH_QUERY = "/search?q";
public static void main(String[] args) throws IOException {
String url = "https://www.google.com/search?site=imghp&tbm=isch&source=hp&q=audi&gws_rd=cr";
Document doc = Jsoup.connect(url).get();
// Response with relevant data is in a <script> tag
Elements elements = doc.select("script[nonce]");
String jsonDataElement = getRelevantScriptTagContainingUrlDataAsJson(elements);
String jsonData = getJsonData(jsonDataElement);
List<String> imageUrls = getImageUrls(jsonData);
int fileId = 1;
for (String urlEntry : imageUrls) {
try {
writeToFile(fileId,makeImageRequest(urlEntry));
System.out.println(urlEntry + " : " + fileId);
fileId++;
} catch (IOException e) {
e.printStackTrace();
}
}
}
private static String getRelevantScriptTagContainingUrlDataAsJson(Elements elements) {
String jsonDataElement = "";
for (Element element : elements) {
String jsonData = element.data();
if (jsonData.startsWith(RELEVANT_JSON_START) && jsonData.contains(PARTIAL_GENERIC_SEARCH_QUERY)) {
jsonDataElement = jsonData;
break;
}
}
return jsonDataElement;
}
private static String getJsonData(String jsonDataElement) {
String jsonData = jsonDataElement.substring(RELEVANT_JSON_START.length(),jsonDataElement.length() - 2);
return jsonData;
}
private static List<String> getImageUrls(String jsonData) {
// Reason for doing this in two steps is debugging is much faster on the smaller subset of json data
String urlArraysList = JsonPath.read(jsonData,"$.data[31][*][12][2][*]").toString();
List<String> imageUrls = JsonPath.read(urlArraysList,"$.[*][*][3][0]");
return imageUrls;
};
private static void writeToFile(int i,HttpURLConnection response) throws IOException {
// opens input stream from the HTTP connection
InputStream inputStream = response.getInputStream();
// opens an output stream to save into file
FileOutputStream outputStream = new FileOutputStream("image_" + i + ".jpg");
int bytesRead = -1;
byte[] buffer = new byte[BUFFER_SIZE];
while ((bytesRead = inputStream.read(buffer)) != -1) {
outputStream.write(buffer,bytesRead);
}
outputStream.close();
inputStream.close();
System.out.println("File downloaded");
}
// Could use JSoup here but I'm re-using this from an earlier answer
private static HttpURLConnection makeImageRequest(String imageUrlString) throws IOException {
URL imageUrl = new URL(imageUrlString);
HttpURLConnection response = (HttpURLConnection) imageUrl.openConnection();
response.setRequestMethod("GET");
response.setConnectTimeout(TIMEOUT);
response.setReadTimeout(TIMEOUT);
response.connect();
return response;
}
}
我测试的部分结果
我已经使用JsonPath过滤了相关的节点,这在您只关心JSON的一小部分而又不想反序列化整个对象时非常有用。它遵循与DOM / XPath / jQuery导航类似的导航样式。
除了这个库和Jsoup之外,所使用的库都是非常糟糕的标准。
祝你好运!
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。