mirror of https://github.com/scrapy/scrapy.git
add xpath playground support for docs
This commit is contained in:
parent
fad6b70d92
commit
bfcc2395e3
|
|
@ -0,0 +1,51 @@
|
|||
from docutils import nodes
|
||||
from sphinx.util.compat import Directive
|
||||
|
||||
|
||||
class xpath_demo_node(nodes.Element):
|
||||
pass
|
||||
|
||||
|
||||
class XPathDemoDirective(Directive):
|
||||
has_content = True
|
||||
required_arguments = 1
|
||||
optional_arguments = 0
|
||||
final_argument_whitespace = True
|
||||
|
||||
def run(self):
|
||||
demo_node = xpath_demo_node()
|
||||
document = self.state.document
|
||||
env = document.settings.env
|
||||
|
||||
_, demo_node.form_template = env.relfn2path('/_static/html/xpathdemo.html')
|
||||
demo_node.xpath_expression = self.arguments[0]
|
||||
demo_node.html_input = '\n'.join(self.content)
|
||||
return [demo_node]
|
||||
|
||||
|
||||
def setup(app):
|
||||
app.add_node(
|
||||
xpath_demo_node,
|
||||
html=(
|
||||
visit_xpath_demo_node,
|
||||
depart_xpath_demo_node,
|
||||
)
|
||||
)
|
||||
app.add_directive('xpathdemo', XPathDemoDirective)
|
||||
|
||||
|
||||
def visit_xpath_demo_node(self, node):
|
||||
try:
|
||||
with open(node.form_template) as fp:
|
||||
s = fp.read().format(
|
||||
xpath_expression=node.xpath_expression,
|
||||
html_input=node.html_input,
|
||||
id=id(node),
|
||||
)
|
||||
self.body.append(s)
|
||||
except AttributeError as e:
|
||||
print('Error: {}'.format(str(e)))
|
||||
|
||||
|
||||
def depart_xpath_demo_node(self, node):
|
||||
pass
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
<div class="xpath_panel">
|
||||
<label for="html_input">HTML Input</label>
|
||||
<textarea id="html_input-{id}" rows="10" class="form-control">{html_input}</textarea>
|
||||
|
||||
<label for="xpath_expression">XPath Expression</label>
|
||||
<input class="form-control xpath_expression"
|
||||
data-target="{id}" type="text" value='{xpath_expression}' />
|
||||
|
||||
<label for="html_output">Result</label>
|
||||
<div id="html_output-{id}" class="html_output"></div>
|
||||
</div>
|
||||
File diff suppressed because one or more lines are too long
|
|
@ -0,0 +1,56 @@
|
|||
.xpath_panel {
|
||||
border: 1px solid black;
|
||||
border-radius: 5px;
|
||||
padding: 10px;
|
||||
margin-top: 20px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.xpath_panel > label {
|
||||
margin-top: 10px;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
.xpath_panel > .html_output {
|
||||
border: solid 1px #8D8D8D;
|
||||
background-color: #F2F2F2;
|
||||
min-height: 25px;
|
||||
border-radius: 5px;
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
.result_node {
|
||||
margin: 10px;
|
||||
border: solid 1px #8D8D8D;
|
||||
background-color: #fff;
|
||||
min-height: 25px;
|
||||
padding: 4px 8px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.result_node + .result_node {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.error_output {
|
||||
color: #FD7C82;
|
||||
border: solid 1px #FD7C82;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.xpath_panel > textarea {
|
||||
min-height: 200px;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
.xpath_panel > input {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.xpath_panel > input,
|
||||
.xpath_panel > textarea {
|
||||
font-family: monospace;
|
||||
font-size: 14px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
$(function(){
|
||||
|
||||
function evalXPath($xpath, $html_input) {
|
||||
var $node = $($.parseXML($html_input.val()));
|
||||
var xpathExpr = $xpath.val();
|
||||
console.log('expr', xpathExpr);
|
||||
return $node.xpath(xpathExpr);
|
||||
}
|
||||
|
||||
function nodeToString(node) {
|
||||
if (node.nodeType == 2)
|
||||
return node.value;
|
||||
if (node.nodeType == 3)
|
||||
return node.wholeText;
|
||||
if (node.nodeType == 8)
|
||||
return "<!--" + node.textContent + "-->";
|
||||
if (node.outerHTML === undefined)
|
||||
return node;
|
||||
return node.outerHTML;
|
||||
}
|
||||
|
||||
function evalXPathUpdateResult() {
|
||||
var $xpath = $(this);
|
||||
var target_id = $xpath.data('target');
|
||||
var $html_input = $('#html_input-' + target_id);
|
||||
var $html_output = $('#html_output-' + target_id);
|
||||
|
||||
try {
|
||||
var nodes = evalXPath($xpath, $html_input);
|
||||
var htmlNodes = $.map(nodes, function(node){
|
||||
return $("<div>").text(nodeToString(node)).addClass("result_node");
|
||||
});
|
||||
|
||||
$html_output.removeClass('error_output');
|
||||
$html_output.html(htmlNodes)
|
||||
console.info('nodes', nodes);
|
||||
} catch (e) {
|
||||
// don't show the "expected expression" message when empty
|
||||
if (e.code === "XPST0003" && $xpath.val() === '') {
|
||||
$html_output.html('');
|
||||
} else {
|
||||
$html_output.addClass('error_output');
|
||||
// TODO: show nicer error message
|
||||
$html_output.html("ERROR: " + e.message);
|
||||
}
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
$('.xpath_expression').on('keyup', evalXPathUpdateResult);
|
||||
$('.xpath_expression').each(evalXPathUpdateResult);
|
||||
})
|
||||
|
|
@ -1,7 +1,11 @@
|
|||
{% extends "!layout.html" %}
|
||||
|
||||
|
||||
{% block footer %}
|
||||
{{ super() }}
|
||||
<script type="text/javascript" src="{{ pathto('_static/jquery.xpath.min.js', 1) }}"></script>
|
||||
<script type="text/javascript" src="{{ pathto('_static/xpathdemo.js', 1) }}"></script>
|
||||
<link rel="stylesheet" href="{{ pathto('_static/xpathdemo.css', 1) }}" type="text/css" />
|
||||
<script type="text/javascript">
|
||||
!function(){var analytics=window.analytics=window.analytics||[];if(!analytics.initialize)if(analytics.invoked)window.console&&console.error&&console.error("Segment snippet included twice.");else{analytics.invoked=!0;analytics.methods=["trackSubmit","trackClick","trackLink","trackForm","pageview","identify","reset","group","track","ready","alias","page","once","off","on"];analytics.factory=function(t){return function(){var e=Array.prototype.slice.call(arguments);e.unshift(t);analytics.push(e);return analytics}};for(var t=0;t<analytics.methods.length;t++){var e=analytics.methods[t];analytics[e]=analytics.factory(e)}analytics.load=function(t){var e=document.createElement("script");e.type="text/javascript";e.async=!0;e.src=("https:"===document.location.protocol?"https://":"http://")+"cdn.segment.com/analytics.js/v1/"+t+"/analytics.min.js";var n=document.getElementsByTagName("script")[0];n.parentNode.insertBefore(e,n)};analytics.SNIPPET_VERSION="3.1.0";
|
||||
analytics.load("8UDQfnf3cyFSTsM4YANnW5sXmgZVILbA");
|
||||
|
|
|
|||
|
|
@ -28,7 +28,8 @@ sys.path.insert(0, path.dirname(path.dirname(__file__)))
|
|||
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
|
||||
extensions = [
|
||||
'scrapydocs',
|
||||
'sphinx.ext.autodoc'
|
||||
'sphinx.ext.autodoc',
|
||||
'xpathdemo',
|
||||
]
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
|
|
|
|||
|
|
@ -159,6 +159,8 @@ Solving specific problems
|
|||
topics/autothrottle
|
||||
topics/benchmarking
|
||||
topics/jobs
|
||||
topics/xpath-tutorial
|
||||
|
||||
|
||||
:doc:`faq`
|
||||
Get answers to most frequently asked questions.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,28 @@
|
|||
==============
|
||||
XPath Tutorial
|
||||
==============
|
||||
|
||||
Welcome to the xpath tutorial. Have a look at this expression:
|
||||
|
||||
.. xpathdemo:: //h2/a
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<title>My page</title>
|
||||
</head>
|
||||
<body>
|
||||
<h2>Welcome to my <a href="#">page</a></h2>
|
||||
<p>This is the first paragraph</p>.
|
||||
<!-- this is the end -->
|
||||
</body>
|
||||
</html>
|
||||
|
||||
And this one :-) :
|
||||
|
||||
.. xpathdemo:: //h2
|
||||
|
||||
<html>
|
||||
<h2>Welcome to my <a href="#">page</a></h2>
|
||||
<p>This is the first paragraph</p>.
|
||||
<!-- this is the end -->
|
||||
</html>
|
||||
Loading…
Reference in New Issue