我正在尝试使用Windows窗体应用程序创建一个webscraper工具。是否有API或方法显示所选文本的确切xpath位置?到目前为止,我编写的代码允许我在webbrowser控件中的导航网站上突出显示文本,并使用ContextMenuStrip将此突出显示的文本输出到richtextbox中。
我在下面写的代码是:
private void getSelectedTextToolStripMenuItem_Click(object sender, EventArgs e)
{
IHTMLDocument2 htmlDocument = webBrowser1.Document.DomDocument as IHTMLDocument2;
IHTMLSelectionObject currentSelection = htmlDocument.selection;
if (currentSelection != null)
{
IHTMLTxtRange range = currentSelection.createRange() as IHTMLTxtRange;
if (range != null)
{
richTextBox1.Text = range.htmlText;
}
按钮导航到以下网站:
private void button1_Click(object sender, EventArgs e)
{
this.webBrowser1.Navigate("https://uk.finance.yahoo.com/q?s=%5EFTSE");
webBrowser1.DocumentCompleted +=
webBrowser1_DocumentCompleted;
}
到目前为止,它完全符合我的要求。但是,我现在希望获得突出显示的任何内容的xpath位置,而不是仅输出文本内容。我的想法是,如果我想提取实时数据(即Yahoo Finance网页上的市场数据),网站上的数据会不断变化,所以我有兴趣获得html页面结构中的位置。关于这是否可能以及我应该遵循哪些步骤的任何想法?
答案 0 :(得分:1)
这是可能的,但你必须通过从所选元素上升层次结构来自己构建XPath,通过执行以下操作:
private void getSelectedXPathToolStripMenuItem_Click(object sender, EventArgs e)
{
var doc = (IHTMLDocument2)webBrowser1.Document.DomDocument;
IHTMLElement selectedElement = null;
var sel = doc.selection;
if (sel.type == "Text")
selectedElement = ((IHTMLTxtRange)sel.createRange()).parentElement();
else if (sel.type == "Control")
selectedElement = ((IHTMLControlRange)sel.createRange()).commonParentElement();
var node = (IHTMLDOMNode)selectedElement;
MessageBox.Show(GetXPath(node, true));
}
string GetXPath(IHTMLDOMNode node, bool stopAtId)
{
var path = new Stack<string>();
while (node != null && node as IHTMLDocument2 == null)
{
var index = 0;
// find previous siblings with the same tag name
var prev = node.previousSibling;
while (prev != null)
{
if (prev.nodeType == 1 && prev.nodeName == node.nodeName)
index++;
prev = prev.previousSibling;
}
var showIndex = index > 0;
// if there were none, find if there are any next siblings with the same tag name
var next = node.nextSibling;
while (next != null)
{
if (next.nodeType == 1 && next.nodeName == node.nodeName)
{
showIndex = true;
break;
}
next = next.nextSibling;
}
var id = ((IHTMLDOMAttribute2)((IHTMLAttributeCollection2)node.attributes).getNamedItem("id")).value;
if (id != string.Empty)
{
showIndex = false;
}
var part = node.nodeName + (showIndex ? string.Format("[{0}]", index + 1) : string.Empty) + (id != string.Empty ? string.Format("[@id = '{0}']", id) : string.Empty);
if (id != string.Empty && stopAtId)
part = "/" + part;
path.Push(part);
if (id != string.Empty && stopAtId)
break;
node = node.parentNode;
}
return "/" + string.Join("/", path);
}
在这个例子中,我创建了一个名为getSelectedXPathToolStripMenuItem_Click
的新方法,该方法对应于上下文菜单中的新菜单项,以在消息框中的选择中显示XPath。显然,如果需要,您可以将其更改为将其放入RTB中。
主要工作在GetXPath
方法中完成,该方法执行DOM遍历。它会检查以前的兄弟节点,以确定节点的索引,如果它是具有该名称的第一个兄弟节点,它也会检查下一个兄弟节点,以查看是否应该包含索引(1)。
它还接受一个名为stopAtId
的布尔参数,顾名思义,当节点设置了id
属性时,它将停止遍历DOM。这可能很有用,因为你总是可以通过它的id很容易地找到一个元素,而不需要知道它的祖先等等。