Android - 使用 JSOUP 解析 JS 生成的 url
Posted
技术标签:
【中文标题】Android - 使用 JSOUP 解析 JS 生成的 url【英文标题】:Android - Parse JS generated urls with JSOUP 【发布时间】:2017-01-01 13:57:05 【问题描述】:我试图解析由 Bootstrap 的 Bootpage.js 生成的 url,看起来像 https://example.com/#page-2 但 JSOUP 无法解析它并显示主 url。 如何从 Bootpage 中获取正常链接或如何让 JSOUP 解析它。
解析代码:
Jsoup.connect("https://example.com/#page-2").followRedirects(true).get();
【问题讨论】:
Jsoup 没有 js 引擎。如果没有指向真实网站的链接,很难提供帮助,但页面可能是动态生成的,因此 JSoup 不是这里的正确工具(检查浏览器中禁用 javascript 的行为)。尝试使用支持 js 的无头浏览器,如 htmlUnit、PhantomJS、... 没有JS显示空白,不重定向到url 那么你用 HtmlUnit 试过了吗?你仍然可以使用 JSoup 来解析结果(参见:***.com/questions/37670840/…)。 不,不工作,有人说它s framework hashed links. Bootpage
s 框架,那么有什么办法可以很好地处理散列链接
还是不愿意分享链接?
【参考方案1】:
(请参阅下面的更新,第一个/接受的解决方案不符合 android 要求,但留作参考。)
桌面解决方案
HtmlUnit 似乎无法处理这个网站(最近经常出现这种情况)。所以我也没有一个简单的java解决方案,但你可以使用PhantomJS:download the binary作为你的操作系统,创建一个脚本文件,从你的java代码中启动进程并使用像@这样的dom解析器解析输出987654323@.
脚本文件(这里称为 simple.js):
var page = require('webpage').create();
var fs = require('fs');
var system = require('system');
var url = "";
var fileName = "output";
// first parameter: url
// second parameter: filename for output
console.log("args length: " + system.args.length);
if (system.args.length > 1)
url=system.args[1];
if (system.args.length > 2)
fileName=system.args[2];
if(url==="")
phantom.exit();
page.settings.userAgent = 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2062.120 Safari/537.36';
page.settings.loadImages = false;
page.open(url, function(status)
console.log("Status: " + status);
if(status === "success")
var path = fileName+'.html';
fs.write(path, page.content, 'w');
phantom.exit();
);
Java 代码(获取标题和封面网址的示例):
try
//change path to phantomjs binary and your script file
String outputFileName = "srulad";
String phantomJSPath = "phantomjs" + File.separator + "bin" + File.separator + "phantomjs";
String scriptFile = "simple.js";
String urlParameter = "http://srulad.com/#page-2";
new File(outputFileName+".html").delete();
Process process = Runtime.getRuntime().exec(phantomJSPath + " " + scriptFile + " " + urlParameter + " " + outputFileName);
process.waitFor();
Document doc = Jsoup.parse(new File(outputFileName + ".html"),"UTF-8"); // output.html is created by phantom.js, same path as page.js
Elements elements = doc.select("#list_page-2 > div");
for (Element element : elements)
System.out.println(element.select("div.l-description.float-left > div:nth-child(1) > a").first().attr("title"));
System.out.println(element.select("div.l-image.float-left > a > img.lazy").first().attr("data-original"));
catch (IOException | InterruptedException e)
e.printStackTrace();
输出:
სიყვარული და მოწყალება / Love & Mercy
http://srulad.com/assets/uploads/42410_Love_and_Mercy.jpg
მუზა / The Muse
http://srulad.com/assets/uploads/43164_large_qRzsimNz0eDyFLFJcbVLIxlqii.jpg
...
更新
使用WebView 和jsoup 可以在Android 中解析具有基于javascript 的动态内容的网站。 以下示例应用程序使用启用了 javascript 的 WebView 来呈现依赖于 Javascript 的网站。使用 JavascriptInterface 返回 html 源代码,并使用 jsoup 进行解析,并且作为概念证明,封面图像的标题和 url 用于填充 ListView。按钮递减或递增触发 ListView 更新的页码。 注意:在 Android 5.1.1/API 22 设备上测试。
向您的 AndroidManifest.xml 添加互联网权限
<uses-permission android:name="android.permission.INTERNET" />
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_
android:layout_>
<LinearLayout
android:orientation="horizontal"
android:layout_
android:layout_>
<Button
android:layout_
android:layout_
android:text="@string/page_down"
android:id="@+id/buttonDown"
android:layout_weight="0.5" />
<Button
android:layout_
android:layout_
android:text="@string/page_up"
android:id="@+id/buttonUp"
android:layout_weight="0.5" />
</LinearLayout>
<ListView
android:layout_
android:layout_
android:id="@+id/listView"
android:layout_gravity="bottom"
android:layout_weight="0.5" />
</LinearLayout>
MainActivity.java
public class MainActivity extends AppCompatActivity
private final Handler uiHandler = new Handler();
private ArrayAdapter<String> adapter;
private ArrayList<String> entries = new ArrayList<>();
private ProgressDialog progressDialog;
private class JSHtmlInterface
@android.webkit.JavascriptInterface
public void showHTML(String html)
final String htmlContent = html;
uiHandler.post(
new Runnable()
@Override
public void run()
Document doc = Jsoup.parse(htmlContent);
Elements elements = doc.select("#online_movies > div > div");
entries.clear();
for (Element element : elements)
String title = element.select("div.l-description.float-left > div:nth-child(1) > a").first().attr("title");
String imgUrl = element.select("div.l-image.float-left > a > img.lazy").first().attr("data-original");
entries.add(title + "\n" + imgUrl);
adapter.notifyDataSetChanged();
);
@Override
protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ListView listView = (ListView) findViewById(R.id.listView);
adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, android.R.id.text1, entries);
listView.setAdapter(adapter);
progressDialog = ProgressDialog.show(this, "Loading","Please wait...", true);
progressDialog.setCancelable(false);
try
final WebView browser = new WebView(this);
browser.setVisibility(View.INVISIBLE);
browser.setLayerType(View.LAYER_TYPE_NONE,null);
browser.getSettings().setJavaScriptEnabled(true);
browser.getSettings().setBlockNetworkImage(true);
browser.getSettings().setDomStorageEnabled(false);
browser.getSettings().setCacheMode(WebSettings.LOAD_NO_CACHE);
browser.getSettings().setLoadsImagesAutomatically(false);
browser.getSettings().setGeolocationEnabled(false);
browser.getSettings().setSupportZoom(false);
browser.addJavascriptInterface(new JSHtmlInterface(), "JSBridge");
browser.setWebViewClient(
new WebViewClient()
@Override
public void onPageStarted(WebView view, String url, Bitmap favicon)
progressDialog.show();
super.onPageStarted(view, url, favicon);
@Override
public void onPageFinished(WebView view, String url)
browser.loadUrl("javascript:window.JSBridge.showHTML('<html>'+document.getElementsByTagName('html')[0].innerHTML+'</html>');");
progressDialog.dismiss();
);
findViewById(R.id.buttonDown).setOnClickListener(new View.OnClickListener()
@Override
public void onClick(View view)
uiHandler.post(new Runnable()
@Override
public void run()
int page = Integer.parseInt(browser.getUrl().split("-")[1]);
int newPage = page > 1 ? page-1 : 1;
browser.loadUrl("http://srulad.com/#page-" + newPage);
browser.loadUrl(browser.getUrl()); // not sure why this is needed, but doesn't update without it on my device
if(getSupportActionBar()!=null) getSupportActionBar().setTitle(browser.getUrl());
);
);
findViewById(R.id.buttonUp).setOnClickListener(new View.OnClickListener()
@Override
public void onClick(View view)
uiHandler.post(new Runnable()
@Override
public void run()
int page = Integer.parseInt(browser.getUrl().split("-")[1]);
int newPage = page+1;
browser.loadUrl("http://srulad.com/#page-" + newPage);
browser.loadUrl(browser.getUrl()); // not sure why this is needed, but doesn't update without it on my device
if(getSupportActionBar()!=null) getSupportActionBar().setTitle(browser.getUrl());
);
);
browser.loadUrl("http://srulad.com/#page-1");
if(getSupportActionBar()!=null) getSupportActionBar().setTitle(browser.getUrl());
catch (Exception e)
e.printStackTrace();
【讨论】:
哇,太棒了,非常感谢,但是这里有一个问题,网站不是我的,我无法将 script.js 粘贴到主服务器中,那么在哪里粘贴呢? 不过,我仍会将其标记为已接受的答案,因为没有找到其他解决方案,对于那些拥有自己网站的人来说,这是非常有用的解决方案。 脚本文件在你的本地机器上运行,它只是你写PhantomJS指令的方式,所以不需要服务器访问。 好吧,我没看懂你的代码,也许是因为我是工作室的初学者等等......此外,它需要外部资源。好吧,我用 webView 实现了它,在 PostExecute 上初始化了下一页,所以它随时可以开始。无论如何,感谢您的大力支持和花时间解决我的问题! 查看答案更新:添加了一个用于解析 javascript 相关页面的工作解决方案。以上是关于Android - 使用 JSOUP 解析 JS 生成的 url的主要内容,如果未能解决你的问题,请参考以下文章
将带有 jsoup 的 HTML 表解析为 android listview