webmagic 模拟登录spider能获取结果吗

刚刚开始学,很多东西可能理解错了,还请各位指教
一些基本类:
Request:包含要爬行的url和一些附加信息,是Page的一个成员变量
&& 主要成员变量
String &url
Map&String, Object& &extras 存储附加信息
long &priority& 优先级 值越大越优先
Request(String url) { this.url = }构造函数
Request &setPriority(long &priority)& 设置优先级需要PriorityScheduler管理
Object &getExtra(String key)获取想要的附加信息 通过key在map里寻找
Request &putExtra(String key, Object value) 添加一组附加信息
ResultItems:包含抓取的结果,是Page类的一个成员变量,并且要在pipeline中处理
主要成员变量
Map&String, Object& fields& 是HashMap
boolean &skip 是否要跳过这条结果,跳过了就不会被pipeline处理
&T& T get(String key) &通过key获取相应值
Map&String, Object& &getAll() { }
ResultItems &put(String key, T value) 给当前对象添加一对值就是field添加记录
Request &getRequest() { }
Page:包含页面抽取的结果和要去取的URL
&& 主要成员变量
Request && 主要就是url对象
ResultItems &resultItems& 抓取的结果,用于持久化
String &rawT
Selectable &
Int &statusC
List&Request& &targetRequests
&& 主要方法
Selectable &getUrl(){ &} 获取当前页url&&&&&&&&&&&&&&&&
Html &getHtml()获取当前页内容&&&&&&&&&&&&&&&
ResultItems &getResultItems(){return resultI }获得抓取的结果以在Pipeline持久化里使用
void& putField(String, Object)保存抓取的结果
void &addTargetRequests(List&String& &requests) 添加url 去抓取
void &addTargetRequests(List&String& &requests, long priority) 添加url 去抓取
void &addTargetRequest(String &requestString) 添加url 去抓取
void &addTargetRequest(Request request) 添加request 去抓取
Site:包含爬虫的一些配置
&& 主要成员变量
List&Request& &startRequests = new ArrayList&Request&();& 爬虫开始的url集
Map&String, String& &cookies = new LinkedHashMap&String, String&();
Map&String, String& &headers = new HashMap&String, String&()
String &userA
int &sleepTime = 5000;
int &retryTimes = 0;
int &cycleRetryTimes = 0;
int &timeOut = 5000;
HttpHost &httpProxy
Site me() { return new Site(); } 新生成一个Site
Site &addCookie(String name, String value) &说明Add a cookie with domain
Site &setDomain(String domain) 设置Site的domain
Site &addStartRequest(Request &startRequest) 添加一条url到开始集里
Site &addHeader(String key, String value) 为下载器设置一个http头
Task &toTask()转换为一个Task
Task:标识不同任务的接口(是个interface),没有成员变量
& 主要方法
String &getUUID() 获取一个task的唯一ID
Site &&&getSite()& 获取一个task的Site
Spider:爬虫入口,包含Downloader&& Scheduler& PageProcessor &and &Pipeline
&& 主要成员变量
Downloader &downloader;
List&Pipeline& &pipelines = new ArrayList&Pipeline&();
Scheduler &scheduler = new QueueScheduler();
PageProcessor &pageP
List&Request& &startRequests;
Site &site;
String &uuid;
Logger logger = Logger.getLogger(getClass());
int &threadNum = 1;
& 主要方法
Spider &create(PageProcessor pageProcessor) 创建一个爬虫
Spider &startUrls(List&String& startUrls)添加开始集合 也就是给startRequests赋值
Spider &startRequest(List&Request& &startRequests) 添加开始集合,给startRequests赋值
Spider &scheduler(Scheduler scheduler)& 设置scheduler(url管理器),给scheduler赋值
Spider &setScheduler(Scheduler scheduler) 设置scheduler(url管理器) ,给scheduler赋值
Spider &pipeline(Pipeline pipeline) 设置pipeline(持久化),给pipelines队列添加一条
Spider &addPipeline(Pipeline pipeline) 设置pipeline(持久化),给pipelines队列添加一条
Spider &clearPipeline() 清空pipeline
Spider &downloader(Downloader downloader) 设置downloader(下载器),给downloader赋值
Spider &setDownloader(Downloader downloader)设置downloader,给downloader赋值
void& test(String... urls) 该方法只抓取一个单独的页面,用于测试抽取效果
void &processRequest(Request request){& //核心处理流程
&&&&& Page page = downloader.download(request, this);
&&&&& pageProcessor.process(page);
&&&&& extractAndAddRequests(page);
&&&&& for (Pipeline pipeline : pipelines)
&&&&&&& pipeline.process(page.getResultItems(), this);
一个简单例子
Spider.create(new SimplePageProcessor("http://my.oschina.net/",
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& &"http://my.oschina.net/*blog/*")).run()
如果想把结果存到文件里怎么办呢?
Spider.create(new &SimplePageProcessor("http://my.oschina.net/",
&&"http://my.oschina.net/*blog/*")) .pipeline(new FilePipeline("/data/temp/webmagic/")).run();
使用FileCacheQueueScheduler在文件中存取url和游标以便关闭后重新继续开始
Spider.create(new SimplePageProcessor("http://my.oschina.net/",
&&"http://my.oschina.net/*blog/*"))
&.scheduler(new FileCacheQueueScheduler("/data/temp/webmagic/cache/")).run();
webmagic四大模块代码解析
downloader文件件夹& ,download一般不用管
Downloader是一个接口
Page &download(Request request, Task task);下载页面并存到Page对象
void &setThread(int threadNum); 设置线程数
HttpClientDownloader 继承Downloader接口
主要成员变量
Logger &logger = Logger.getLogger(getClass());
Map&String, CloseableHttpClient& &httpClients = new HashMap&String, CloseableHttpClient&();
HttpClientGenerator &httpClientGenerator = new HttpClientGenerator();
&& 主要方法
Html &download(String url)& 下载html
Html &download(String url, String charset)
Page &download(Request request, Task task) 实现接口方法
HttpClientGenerator 这个类是HttpClient产生器,就是工厂
主要成员变量
PoolingHttpClientConnectionManager &connectionM
CloseableHttpClient &&generateClient(Site site)
CloseableHttpClient& &getClient(Site site)
HttpClientGenerator &setPoolSize(int poolSize)
void &generateCookie(HttpClientBuilder httpClientBuilder, Site site)
目前有几个Downloader的实现:
&HttpClientDownloader
集成了Apache HttpClient的Downloader。Apache HttpClient(4.0后整合到HttpCompenent项目中)是强大的Java http下载器,它支持自定义HTTP头(对于爬虫比较有用的就是User-agent、cookie等)、自动redirect、连接复用、cookie保留、设置代理等诸多强大的功能。
SeleniumDownloader(这个文件里没有)
对于一些Javascript动态加载的网页,仅仅使用http模拟下载工具,并不能取到页面的内容。这方面的思路有两种:一种是抽丝剥茧,分析js的逻辑,再用爬虫去重现它;另一种就是:内置一个浏览器,直接获取最后加载完的页面。webmagic-selenium包中整合了Selenium到SeleniumDownloader,可以直接进行动态载页面的抓取。使用selenium需要安装一些native的工具,具体步骤可以参考作者的博文使用Selenium来抓取动态加载的页面
PageProcessor文件夹,页面分析及链接抽取,是我们使用的关键
PageProcessor是一个接口,用于定制爬虫,可定制启动url和一些设置,如何发现要抓取的url,数据要如何提取和存储
&& 主要方法
void &process(Page page); 处理Page对象,提取URL,提取数据并存储
Site &getSite() 获取site 的一些设置信息
通过编写一个实现PageProcessor接口的类,就可以定制一个自己的爬虫,比如
public &class &GithubRepoPageProcesser implements &PageProcessor {
private &Site &site = Site.me().setRetryTimes(3).setSleepTime(100); //一些设置信息
&@Override
&public void &process(Page &page) { //通过正则表达式和Xpath来抓取
&& //要抓取的链接需要显示地添加
&& page.addTargetRequests(page.getHtml().links().regex("(https://github\\.com/\\w+/\\w+)").all());
&& //抓取规则
&& page.putField("author", page.getUrl().regex("https://github\\.com/(\\w+)/.*").toString());
&& page.putField("name", page.getHtml().xpath("//h1[@class='entry-title public']/strong/a/text()").toString());
& &if (page.getResultItems().get("name")==null){& page.setSkip(true);& } //skip this page
&& page.putField("readme", page.getHtml().xpath("//div[@id='readme']/tidyText()"));
&public Site &getSite() {
public &static &void &main(String[] &args) {
&&&& &Spider.create(new GithubRepoPageProcesser()).addUrl("/code4craft").thread(5).run();
几种抓取方式
1.java正则表达式:这个不多讲 .& []& ()& 等
2.Jsoup支持jquery中css selector的方式选取元素 :
& &String content = "blabla"; Document doc = JSoup.parse(content);& Elements links = doc.select("a[href]");
3. HtmlCleaner支持XPath的方式选取元素:
4.Xpath: tagNode.evaluateXPath("//a/ ")
5.CSS选择:$("a[href]").attr("href")
webmagic主要实现了CSS Selector、XPath和正则表达式三种抓取方式,其实现放在Selector文件下
Scheduler文件夹,URL管理 主要有两个实现QueueScheduler和FileCacheQueueScheduler
Scheduler是一个接口,实现它可以管理要抓取的URL,还可以去重复
& 主要方法
void push(Request request, Task task)& 添加一个要抓取的url
Request poll(Task task)& 取出一个URL来爬行
QueueScheduler继承Scheduler接口,一个简单的内存队列,速度较快,并且是线程安全的
主要成员变量
Logger &logger = Logger.getLogger(getClass());
BlockingQueue&Request& &queue = new LinkedBlockingQueue&Request&();
Set&String& &urls = new HashSet&String&();
&& 主要方法
void &push(Request request, Task task)& 往queue里边添加一个request
Request &poll(Task task)& 从queue里边弹出一个request
PriorityScheduler继承Scheduler接口,是一个文件队列,它可以用于耗时较长的下载任务,在任务中途停止后,下次执行仍然从中止的URL开始继续爬取。
主要成员变量
Logger &logger = Logger.getLogger(getClass());
BlockingQueue&Request& &noPriorityQueue = new &LinkedBlockingQueue&Request&();
Set&String& &urls = new HashSet&String&();
void &push(Request request, Task task) 添加一个request
Request &poll(Task task) 弹出一个request
Pipeline文件夹,用于后续处理和持久化 主要有四个实现
Pipeline是一个接口,用于持久化 和 离线处理& 只有一个方法
& 主要方法
void &process(ResultItems&resultItems, Task &task) 从resultItems通过get(key)得到数据
CollectorPipeline&T&也是一个接口,且继承了Pipeline,新增了一个方法
&&& 主要方法
List&T& &getCollected(); 获取收集到的所有信息
ConsolePipeline 实现了Pipeline接口,把结果输出到控制台
&public void process(ResultItems resultItems, Task task) {
&&&& System.out.println("get page: " + resultItems.getRequest().getUrl());
&&&& for (Map.Entry&String, Object& entry : resultItems.getAll().entrySet()) {
&&&&&&&&&&& System.out.println(entry.getKey() + ":\t" + entry.getValue());
ResultItemsCollectorPipeline实现了CollectorPipeline&T&接口
主要成员变量
List&ResultItems& &collector = new &ArrayList&ResultItems&();
&&&& 主要方法
public &synchronized &void &process(ResultItems resultItems, Task task) {
&&&&&&& collector.add(resultItems);
public &List&ResultItems& &getCollected() {
FilePipeline 实现了FilePersistentBase,Pipeline接口,把数据存到文件里,每个URL单独保存到一个页面,以URL的MD5结果作为文件名
&& 主要成员变量
Logger &logger = Logger.getLogger(getClass());
&& 主要方法
FilePipeline() {& setPath("/data/webmagic/");& } 默认构造函数 设置了存储路径
FilePipeline(String &path) {& setPath(path);& } 自定义存储路径
void &process(ResultItems resultItems, Task task)
selector文件夹,算是PageProcessor的扩展吧,因为要集合很多选取方式
Selectable:接口 可选的文本& & & & & & & & & & & & & & & & & & & & & & &
Selectable &xpath(String xpath);使用xpath选择
Selectable &$(String selector);& 使用CSS选择
Selectable &$(String selector, String attrName); 使用CSS选择
Selectable &css(String selector); 使用CSS选择
Selectable &css(String selector, String attrName); 使用CSS选择
Selectable &regex(String regex);使用正则表达式选择group默认为1
Selectable &regex(String regex, int group); 使用正则表达式选择
Selectable &replace(String regex, String replacement);
Selectable &smartContent(); 选取smart content
Selectable &links(); 选取所有链接
PlainText:实现了Selectable接口
PlainText(List&String& strings)
PlainText(String text)
PlainText &create(String text)
Selectable &select(Selector selector, List&String& strings)
Selectable &selectList(Selector selector, List&String& strings)
Html:继承了PlainText
主要成员变量
Logger &logger = Logger.getLogger(getClass());
Document & 存储解析过的文档
boolean &init =
主要方法:给出新增的方法
Html(List&String& strings)
Html(String text)
Html(Document document)
void &&initDocument() 简单的初始化方法
Html &create(String text)
String &selectDocument(Selector selector)
List&String& &selectDocumentForList(Selector selector)
String &getText()
Document &getDocument()
Selectors:一个抽象类
RegexSelector &regex(String expr)&&&&&&& RegexSelector &regex(String expr, int group)
CssSelector &$(String expr)&&&&&&&&&&&&& CssSelector &$(String expr, String attrName)
XpathSelector &xpath(String expr)&&&&&&& XsoupSelector &xsoup(String expr)
AndSelector &and(Selector... selectors)&&& OrSelector &or(Selector... selectors)
SmartContentSelector &smartContent()&
Selector:接口,文本抽取器 就两个方法
String &select(String text) 抽取单条结果,若有多个,只有第一个被选择
List&String& &selectList(String text) 抽取所有结果
ElementSelector:接口&& 针对html elements
String select(Element element);
List&String& selectList(Element element);
RegexSelector:实现Selector接口,使用正则表达式抽取
主要成员变量
String &regexS
int &group = 1;
RegexSelector(String regexStr, int group)构造
RegexSelector(String regexStr)构造
String &select(String text)重写
List&String& &selectList(String text)重写
RegexResult &selectGroup(String text)
List&RegexResult& &selectGroupList(String text)
XpathSelector:实现Selector接口,使用Xpath抽取
主要成员变量
String &xpathS
主要成员方法
XpathSelector(String xpathStr)
String &select(String text)重写
List&String& &selectList(String text)重写
ReplaceSelector:实现Selector接口,重置选择器
主要成员变量
String &regexS
主要成员方法
ReplaceSelector(String regexStr, String replacement)
String &select(String text)重写
List&String& &selectList(String text)重写
&AndSelector:实现Selector接口,所有选择器以管道式安排,后一个用前一个的结果
主要成员变量
List&Selector& &selectors = new ArrayList&Selector&()
主要成员方法
AndSelector(Selector... selectors)
AndSelector(List&Selector& selectors)
String &select(String text)重写
List&String& &selectList(String text)重写
OrSelector:实现Selector接口,所有提取器独自工作,提取的结果将会整合起来
主要成员变量
List&Selector& &selectors = new ArrayList&Selector&()
主要成员方法
OrSelector(Selector... selectors)
OrSelector(List&Selector& selectors)
String &select(String text)重写
List&String& &selectList(String text)重写
OrSelector:实现Selector接口,所有提取器独自工作,提取的结果将会整合起来
主要成员变量
List&Selector& &selectors = new ArrayList&Selector&()
主要成员方法
OrSelector(Selector... selectors)
OrSelector(List&Selector& selectors)
String &select(String text)重写
List&String& &selectList(String text)重写
BaseElementSelector:抽象类 继承于Selector, ElementSelector
String &select(String text)重写
List&String& &selectList(String text)重写
CssSelector:继承BaseElementSelector&& CSS selector. Based on Jsoup
主要成员变量
String &selectorT
String &attrN
主要成员方法
CssSelector(String selectorText)
CssSelector(String selectorText, String attrName)
String getValue(Element element)
String &select(String text)重写
List&String& &selectList(String text)重写
XsoupSelector:继承BaseElementSelector&& XPath selector based on Xsoup
主要成员变量
XPathEvaluator xPathE
主要成员方法
XsoupSelector(String xpathStr)
String &select(String text)重写
List&String& &selectList(String text)重写
例如,我已经下载了一个页面,现在要抽取某个区域的所有包含"blog"的链接,我可以这样写:
String content = "blabla"; //content是用别的爬虫工具抽取到的正文
List&String& &links = Html.create(content)
.$("div.title")& //css 选择,Java里虽然很少有$符号出现,不过貌似$作为方法名是合法的
.xpath("//@href")& //提取链接
.regex(".*blog.*") //正则匹配过滤
.all(); //转换为string列表
utils文件夹,一些实用工具
UrlUtils:url和html的工具
&&Pattern patternForProtocal = pile("[\\w]+://")
Pattern patternForCharset = pile("charset\\s*=\\s*['\"]*([^\\s;'\"]*)");
& Pattern patternForHref = pile("(&a[^&&]*href=)[\"']{0,1}([^\"'&&", Pattern.CASE_INSENSITIVE);
String canonicalizeUrl(String url, String refer)
& String getHost(String url)
& String removeProtocol(String url)
& String getDomain(String url)
String fixAllRelativeHrefs(String html, String url)
List&Request& convertToRequests(Collection&String& urls)
List&String& convertToUrls(Collection&Request& requests)
String getCharset(String contentType)
ThreadUtils:
& ExecutorService newFixedThreadPool(int threadSize)
NumberUtils:抽象类
& int compareLong(long o1, long o2)
FilePersistentBase:文件持久化的基本对象
& String path
String PATH_SEPERATOR = "/"
& String getPath()
& void checkAndMakeParentDirecotry(String fullName)
File getFile(String fullName)
void setPath(String path)
EnvironmentUtil
&& String USE_XSOUP = "xsoup"
&& boolean useXsoup()
&& void &setUseXsoup(boolean useXsoup)
& 开源中国(OSChina.NET) |
开源中国社区(OSChina.net)是工信部
指定的官方社区spider和oospider有什么区别吗? - 开源中国社区
当前访客身份:游客 [
当前位置:
你好,想跟你请教个问题:spider和oospider有什么区别吗?oospider.get(String URL)这个方法传入参数是个url字符串返回一个实体,但是我想用@TargetUrl@HelpUrl这两个标签该怎么用呢?
共有0个答案
更多开发者职位上
有什么技术问题吗?
wymoso...的其它问题
类似的话题webmagic spider能获取结果吗_百度知道1698人阅读
数据抓取(1)
WebMagic的结构分为Downloader、PageProcessor、Scheduler、Pipeline四大组件,并由Spider将它们彼此组织起来。这四大组件对应爬虫生命周期中的下载、处理、管理和持久化等功能。WebMagic的设计参考了Scapy,但是实现方式更Java化一些。
而Spider则将这几个组件组织起来,让它们可以互相交互,流程化的执行,可以认为Spider是一个大的容器,它也是WebMagic逻辑的核心。
WebMagic总体架构图如下:
1.2.1 WebMagic的四个组件
1.Downloader
Downloader负责从互联网上下载页面,以便后续处理。WebMagic默认使用了作为下载工具。
2.PageProcessor
PageProcessor负责解析页面,抽取有用信息,以及发现新的链接。WebMagic使用作为HTML解析工具,并基于其开发了解析XPath的工具。
在这四个组件中,PageProcessor对于每个站点每个页面都不一样,是需要使用者定制的部分。
3.Scheduler
Scheduler负责管理待抓取的URL,以及一些去重的工作。WebMagic默认提供了JDK的内存队列来管理URL,并用集合来进行去重。也支持使用Redis进行分布式管理。
除非项目有一些特殊的分布式需求,否则无需自己定制Scheduler。
4.Pipeline
Pipeline负责抽取结果的处理,包括计算、持久化到文件、数据库等。WebMagic默认提供了“输出到控制台”和“保存到文件”两种结果处理方案。
Pipeline定义了结果保存的方式,如果你要保存到指定数据库,则需要编写对应的Pipeline。对于一类需求一般只需编写一个Pipeline。
1.2.2 用于数据流转的对象
1. Request
Request是对URL地址的一层封装,一个Request对应一个URL地址。
它是PageProcessor与Downloader交互的载体,也是PageProcessor控制Downloader唯一方式。
除了URL本身外,它还包含一个Key-Value结构的字段extra。你可以在extra中保存一些特殊的属性,然后在其他地方读取,以完成不同的功能。例如附加上一个页面的一些信息等。
Page代表了从Downloader下载到的一个页面——可能是HTML,也可能是JSON或者其他文本格式的内容。
Page是WebMagic抽取过程的核心对象,它提供一些方法可供抽取、结果保存等。在第四章的例子中,我们会详细介绍它的使用。
3. ReusltItems
ReusltItems相当于一个Map,它保存PageProcessor处理的结果,供Pipeline使用。它的API与Map很类似,值得注意的是它有一个字段skip,若设置为true,则不应被Pipeline处理。
1.2.3 控制爬虫运转的引擎--Spider
Spider是WebMagic内部流程的核心。Downloader、PageProcessor、Scheduler、Pipeline都是Spider的一个属性,这些属性是可以自由设置的,通过设置这个属性可以实现不同的功能。Spider也是WebMagic操作的入口,它封装了爬虫的创建、启动、停止、多线程等功能。下面是一个设置各个组件,并且设置多线程和启动的例子。详细的Spider设置请看第四章——。
public static void main(String[] args) {
Spider.create(new GithubRepoPageProcessor())
.addUrl(&/code4craft&)
.setScheduler(new RedisScheduler(&localhost&))
.addPipeline(new JsonFilePipeline(&D:\\data\\webmagic&))
.thread(5)
1.2.4 快速上手
上面介绍了很多组件,但是其实使用者需要关心的没有那么多,因为大部分模块WebMagic已经提供了默认实现。
一般来说,对于编写一个爬虫,PageProcessor是需要编写的部分,而Spider则是创建和控制爬虫的入口。在第四章中,我们会介绍如何通过定制PageProcessr来编写一个爬虫,并通过Spider来启动。
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:18958次
排名:千里之外
原创:25篇
转载:15篇
(1)(1)(1)(4)(32)(1)记webmagic一个多线程问题排查和修复的过程—服务器托管网
记webmagic一个多线程问题排查和修复的过程
在webmagic的多线程抓取中有一个比较麻烦的问题:当Scheduler拿不到url的时候,不能立即退出,需要等到没抓完的线程都运行完毕,没有新url产生时,才能退出。之前使用Thread.sleep来实现,当拿不到url时,sleep一段时间再取,确定没有线程执行之后,再退出。
但是这种方式始终不够优雅。Java里面有wait/notify机制可以解决这种同步的问题。于是webmagic 0.4.0用wait/notify机制代替了之前的Thread.sleep机制。代码如下:
while (!Thread.currentThread().isInterrupted() && stat.get() == STAT_RUNNING) {
Request request = scheduler.poll(this);
if (request == null) {
if (threadAlive.get() == 0 && exitWhenComplete) {
// wait until new url added
waitNewUrl();
final Request requestFinal =
threadAlive.incrementAndGet();
executorService.execute(new Runnable() {
public void run() {
processRequest(requestFinal);
} catch (Exception e) {
logger.error("download " + requestFinal + " error", e);
} finally {
threadAlive.decrementAndGet();
signalNewUrl();
private void waitNewUrl() {
newUrlLock.lock();
newUrlCondition.await();
} catch (InterruptedException e) {
} finally {
newUrlLock.unlock();
这里当线程完成之后,会调用signalNewUrl()来通知主线程,停止等待!
0.4.0发布之后,有用户问我,为什么有的时候抓完无法退出?我开始就怀疑这里可能存在线程安全问题,但是苦于无法复现。
思考了一下,有可能存在这样执行情况:
threadAlive&0,执行if (threadAlive.get() == 0 && exitWhenComplete)check跳过,于是准备进入waitNewUrl();
此时最后一个子线程执行结束,threadAlive.decrementAndGet();和signalNewUrl();相继执行;
此时主线程进入waitNewUrl(),结果已无线程执行,也无人可以notify它了,于是线程一直等待…
那么似乎在lock里加入double-check就OK了?但是今天看了/articles/4576.html这篇文章,大概意思是:出了问题不要靠猜!一定要复现并测试!
于是决定手动模拟!开启10个线程,并mock了所有部件,循环10000次去执行,代码不贴了,地址:/code4craft/webmagic/blob/master/webmagic-core/src/test/java/us/codecraft/webmagic/SpiderTest.java。执行一下,果然到了第13次就卡住了!jstack之后,果然卡在newUrlCondition.await();这里!
然后加入double-check:
private void waitNewUrl() {
newUrlLock.lock();
//double check
if (threadAlive.get() == 0 && exitWhenComplete) {
newUrlCondition.await();
} catch (InterruptedException e) {
} finally {
newUrlLock.unlock();
结果执行成功!至此问题解决!
经过这个例子,也大致明白了为什么wait/notify之前总是要先lock。为什么呢?有机会写一篇文章总结一下吧!
很简单,是吧?其实这篇文章只想说明一件事:出了bug不要靠猜!一定要复现并确认解决!

我要回帖

更多关于 webmagic 获取属性 的文章

 

随机推荐