在抓取网页时遇到了一段报错信息: 抓取网页的代码及网址如下: 在错误信息中提示了网页的编码不是utf-8。那么如何确认网页的编码形式呢?有如下几种方式: 从网页源码中查找chaset信息; 使用FireBug。重新打开网页,使用FireBug的NetWork抓取网页加载过程,查看目标网页的头信息,找到Content-Type,其中的charset信息就是; 使用Firefox右键菜单中的“查看页面信息”功能:点击网页空白处 –> 右键菜单 –> 查看页面信息,在弹出窗口中选择 常规 –> 文字编码 也可以查看网页编码信息。 检测到网页的编码是gbk。修改后就可以了。 #########
[阅读更多...]-
Python网络爬虫6 – 网页编码
-
Python网络爬虫5 – 图片抓取
这一节看下如何抓取网页中的图片。目标网址是:http://pp.163.com/longer-yowoo/pp/10069141.html。这里有一组我非常喜欢的图片。 要抓取网页首先就要找出图片的网址。这里仍然是使用BeautifulSoup,具体如何使用在前一节《使用BeautifulSoup解析网页》时说过,现在就不说了。看下代码好了: 在上面的代码中soup.select(“img[data-lazyload-src]”)一句查询了所有包含data-lazyload-src属性的img标签。在捕捉到图片标签后,又取出data-lazyload-src属性并打印了出来,一共有六个。 然后就是如何抓取图片了。先来看看之前的一段代码: 这段代码的作用是抓取网页内容并转换为字符串。其中,response是http反馈信息,read方法的作用是读取出http返回的字节流,decode则是将字节流转换为字符串。字符串本质是字节流,图片也是。那么,如何获取图片也就清楚了:就是通过http获取到图片的字节流,再将字节流保存到硬盘即可。看下是如何实现的: 注意open方法中的mode属性“wb”,w指的是写文件,b指的是采用二进制模式。 再来看看完整的程序: 上面的代码仍可以优化下:要下载的文件的名称及扩展名最好是从下载链接中动态获取。这里我偷了个懒,随意指定了文件的名称,扩展名则是早已经知道了。 ###################
[阅读更多...] -
Python网络爬虫4 – 多线程抓取
之前的内容已经大致实现了如何获取网页、分析网页、获取目标内容。接下来的目标是如何让网页抓取进行得更效率些。在进行抓取的时候,时间的消耗主要是在请求等待的时间上,所以一个最容易想到的优化方式就是使用多线程。 多线程 多线程的实现还是比较简单的,下面是一个简单的线程实现方案: 就是这么简单。 线程池 在抓取网页的时候,一个简单的思路就是为每个网页启动一个线程。在要抓取的网页比较少的时候——比如百十来个——这样子还是可行的。但是网页比较多的时候,这样做就不太合理了。因为线程的创建启动和运行都会消耗很多的资源,线程启动太多会耗尽资源导致机器卡死。而且,创建线程后只执行一次也是一种浪费。为了减少线程的创建、实现线程的重复利用,我们需要引入线程池。 可以使用python的ThreadPoolExecutor来创建线程池: 也很简单,甚至不比单线程多一行代码。在代码里创建了一个总数为64的线程池,然后在一个循环中每次取出一条线程来执行func函数,没有空闲线程时则会进入等待。 按照这样的思路,我们也可以使用Queue来自己创建线程池: 在上面的代码里创建了一个长度为5的队列,然后参照队列的长度创建了几个线程,并立刻启动。每个线程随时待命,一旦队列里面有了要执行的任务就会拿过来立即执行,并在执行完成后发送通知给队列。queue.join()方法则会在队列中的所有任务执行完成前阻塞住线程,待所有任务执行完成后再继续执行后面的代码。 并行 前面所述的方案是并发处理的方案。并发处理的方案可以充分利用CPU。不过现在的CPU通常都是多核的,为了利用多核CPU的特点,可以考虑使用并行处理的方案。下面是一个进程的演示: 当然也有对应的进程池了: 在我使用爬虫的经历中极少会用到多进程或进程池。不过,既然说起来了就得说个全套,所以才耐着性子把这块儿写完。 很多人可能就是因为要学习爬虫才开始看Python的。不过看了python却只知道爬虫未免有些可惜,我还是希望能够多接触到一些较深入的东西的。多线程和多进程是学习Python的经历中无论如何也不应该绕过的部分,如果不想浅尝而止的话,还是建议多看看。 完整的抓取程序在下面。程序写于半年前,有很多不成熟的地方,也没心思修改了,凑合看吧。因为代码太长所以折叠了起来: 参考文档 python cookbook:http://python3-cookbook.readthedocs.io/zh_CN/latest/c12/p07_creating_thread_pool.html Python 并发编程之使用多线程和多处理器:http://developer.51cto.com/art/201405/438178.htm ####################
[阅读更多...] -
lambda表达式3 – 方法引用
一个实例 使用lambda表达式可以创建简洁的匿名方法。不过,有时候lambda表达式只是简单的调用了已有的方法。此时,使用方法引用无疑是一个更简洁易读的方案。 再来看看之前使用过的Person类: 假设所有的Person对象都保存在一个数组中,然后想按年龄对数组成员进行排序,可以使用如下的代码进行实现: 这里调用的sort方法的声明是这样子的: 注意这里的Comparator是一个函数式接口,因此无需再定义一个PersonAgeComparator类并创建一个实例,直接使用lambda表达式实现即可: 不过Person类中已经有了一个compareByAge方法,因此可以对上面的表达式作进一步的简化: 因为这个lambda表达式只是调用了一个已有的方法,因此可以使用方法引用替换lambda表达式: 这里的方法引用Person::compareByAge和lambda表达式(a, b) -> Person.compareByAge(a, b)在语义上是一样的。它们都有如下的特性: 参数列表copy自Comparator<Person>.compare方法,即(Person, Person); 调用了Person.compareByAge方法。 在这个例子以及下面的示例中可以看到,方法引用使用的场合大致是和lambda表达式重合的。 方法引用的类型 方法引用有四种类型: 类型 示例 静态方法引用 ContainingClass::staticMethodName 实例方法引用 containingObject::instanceMethodName 一个类任意对象的实例方法引用 ContainingType::methodName 构造方法引用 ClassName::new 静态方法引用 前面示例中的Person::compareByAge就是一个静态方法引用。 实例方法引用 下面的代码演示了实例方法引用: 方法引用myComparisonProvider::compareByName中调用的方法compareByName是对象myComparisonProvider的一部分。JRE会推断出方法的参数类型。在这个例子里就是(Person, Person)。 一个类的任意对象的方法引用 如下的代码演示了一个类的任意对象的实例方法引用: 与方法引用String::compareToIgnoreCase对等的lambda表达式需要有(String a, String b)这样的参数列表。这里的a和b只是随意起的名字,只是为了描述参数。方法引用会触发这样的方法:a.compareToIgnoreCase(b)。 构造方法引用 可以使用new关键字像创建静态方法引用一样创建构造方法引用。下面的代码将一个集合中的元素拷贝到了另一个集合: 函数式接口Supplier有一个方法get。这个方法没有任何参数,只是返回一个对象: 可以使用一个lambda表达式调用transferElements : 也可以使用一个构造方法引用来替换lambda表达式: java编译器可以推断出你想创建一个HashSet集合,其中包含的元素类型是Person。当然也可以显式声明: 就这样。 参考文档 https://docs.oracle.com/javase/tutorial/java/javaOO/methodreferences.html #################
[阅读更多...] -
intellij idea git ignore
使用intellij idea时想将一个文件或目录排除在版本控制外,却找不到add to ignore选项。在使用git时,可以直接编辑.ignore文件,但是还是觉得有些不方便。所以安装一个插件好了:settings –> plugins –> Browse repositories,搜索ignore,选第一个.ignore – VCS INTEGRATION,点install按钮,安装完了重启。使用时,选择文件,右键菜单,应该在最下方就可以看到相关的选项。 就这样。
[阅读更多...] -
关于https的一个错误
前两天我维护的服务出了一个问题,在请求一个连接时发生了Exception: 异常大致说的是在进行https请求时,验证证书出了问题。 服务基于JDK1.6,使用httpclient(版本是4.5.1)发送请求,调用的URL是https://talkingdata.qccr.com。 异常中的信息说的是请求的地址(talkingdata.qccr.com)与获得的证书中的hostName(*.qichechaoren.com)不符,因此在SSLPeerUnverifiedException.verifyHostname()方法中抛出了异常。但是在浏览器中发送请求或者是通过curl命令都是能够访问的。最让人郁闷的是在我的机器上使用原程序执行调用也没有报异常;把相关的程序封装成jar扔到服务器上调用还是正常的。 想了一段时间,在curl命令中添加了SSL信息,像这样: 2和3分别表示用的是sslv2和sslv3。果然报错了。后来又发现了SSL的实现还有TLS,试着使用TLS协议请求是正确的: 之前没有想到过SSL协议这里,打开浏览器看了一下这个地址的证书果然是TLS的: 看样子是在处理TLS请求时出了问题。 接下来就是定位问题的出处了。既然程序是可以正常执行的,那么就极有可能是出在容器上。把相关的程序重新封装下扔到jetty中执行果然报异常了。当时认为问题就是出在jetty上了,可能是服务与容器间存在类冲突。容器的类是不好轻易换的,只好改程序了。原程序是这样子的: 需要调整的就是SSL的支持那一块。将对https的验证强行去掉好了。只需要改一处: 现在请求是可以不报异常了。因为所有关于hostName的验证都是返回true 隔天和同事说起这件事,同事提供了一个信息:生产环境上的jetty容器使用的jdk和系统jre是不一样的…. jetty容器使用的是jdk1.6,服务器jre是1.7,我开发用的机器jdk是1.8。当时居然没有想到这上头。问题有可能是出在jre上啊。所以最后将jetty使用的jdk版本升级以后就好了。 今天也尝试着定位问题发生的具体位置来着,后来发现是在handshake的时候,jdk1.6和jdk1.7会返回不同的证书信息。更具体的信息想要获取就有些困难了,因为相关的部分代码是不开源的。 之前本以为是这个地址的证书是采用的TLS1.2,而jdk1.6的大部分版本都是不支持TLS1.2的。后来使用工具分析了请求的地址,发现支持的协议包括TLS1.0、TLS1.1、TLS1.2。而TLS1.0在JDK1.1的时候就已经支持了。 已经在这个问题上用了四分之一个周末的时间了,还有其他事情要做,目前只好暂时放下了。 一个SSL分析的网址:https://www.ssllabs.com/ssltest/analyze.html。 openJdk关于TLS1.2的BUG清单:https://bugs.openjdk.java.net/browse/JDK-6916074。 ##############
[阅读更多...] -
Python网络爬虫3 – 使用BeautifulSoup解析网页
在第一节演示过如何使用正则表达式截取网页内容。不过html是比正则表达式更高一级的语言,仅仅使用正则表达式来获取内容还是有些困难的。 这次会使用一个新的工具:python的BeautifulSoup库,BeautifulSoup是用来从HTML或XML文件中提取数据的工具。 BeautifulSoup需要先安装才能使用。关于BeautifulSoup安装和使用可以参考这份文档:https://www.crummy.com/software/BeautifulSoup/bs4/doc.zh/。 打开我们的搜索结果页https://www.torrentkitty.tv/search/蝙蝠侠/。此时最好使用chrome浏览器,因为chrome浏览器的Developer Tools(类似FireBug)有一个功能可以获取CSS选择器(选中目标 –> Copy –> Copy selector),这是很有用的(有的时候也会出现问题,不过还是可以用来做参考的)。 先使用BeautifulSoup获取title试试: 很简单不是。这里是用了CSS选择器来获取HTML中的内容。 在搜索结果中,点击每个结果项右侧的“open”按钮可以打开下载。使用DeveloperTools可以看到“open”按钮实际上是一个超链接,超链接指向的是一个磁力链接。这个磁力链接就是我们要采集的目标。使用Chrome的DeveloperTools的选择器选中任意一个“open”按钮,然后在Elements项中,找到我们选中的项的源码(很容易找到,选中项的背景是蓝色的),右键 –> Copy –> Copy selector可以获取到这个按钮的CSS选择器: 将这个选择器放到代码中却是不生效的: 执行结果输出的是0。soup.select()方法返回的是一个列表,长度为0……好像不用解释了。 python是不支持上面的选择器的部分语法的,比如nth或者tbody,修改下就可以了: 直接执行上面的代码就可以得到一个超链接的源码: 超链接中的href和title属性就是我们的目标。BeautifulSoup也提供了获取属性的方案,select方法返回的每个值中都包含一个attrs字典,可以从字典中获取到相关的属性信息: 好了,大体就是这样。 不过程序中最难看的就是获取超链接的方案:一个一个地获取是不可能。好在BeautifulSoup支持通过属性的值来获取对象,最后调整下就是这样子了: 上面的代码中的a[href^=”magnet:?xt”]表示查询的是所有<a>标签,且<a>标签的href属性需要以“magnet:?xt”开头。(看到“^”有没有觉得熟悉,这个“^”和正则式中的“^”意义是一样的)。通过这个select方法得到<a>标签列表,然后遍历标签列表,从标签的attrs字典中读取到相关的属性信息。 完整的代码如下: ###############
[阅读更多...] -
intellij idea查看scala sdk的源代码
下载源代码 点击这个链接:http://www.scala-lang.org/download/all.html; 选择需要的版本点击打开; 在新打开的网页下方找到源代码下载项: 在intellij idea设置指向源代码 在intellij中打开File –> Project Structure,快捷键(Ctrl + Alt + Shift + s); 选择Global Libraries –> 目标SDK; 在右侧面板中的Scala Library下方点击“+”按钮; 选择解压后的源码包的src目录; apply即可。 看个截图好了: 参考:http://stackoverflow.com/questions/13520532/attaching-sources-in-intellij-idea-for-scala-project ####
[阅读更多...] -
Python网络爬虫2 – 请求中遇到的几个问题
这次尝试下怎样搜索电影并解析出磁力链接信息。 搜索的网址是:https://www.torrentkitty.tv/search/。 开始了! 使用FireFox打开上面的网址,输入要搜索的电影。在点击搜索按钮前记得打开FireBug,并激活“网络”页签。 查看了请求的详情有些哭笑不得:点击搜索按钮后网页跳转到了这样的地址:https://www.torrentkitty.tv/search/蝙蝠侠/——很明显的REST风格的请求。这样,我们要搜什么内容直接将要搜索的内容拼装到请求地址中就行了。搜索的代码是这样的: 执行后报错了,报错信息如下: 根据错误栈信息可以看出是在发送http请求时报错的,是因为编码导致的错误。在python中使用中文经常会遇到这样的问题。因为是在http请求中出现的中文编码异常,所以可以考虑使用urlencode加密。 在python中对字符串进行urlencode使用的是parse库的quote方法,而非是urlencode方法: 再次执行请求,依然报错了: 报的是HTTP 403错误。这样的错误我遇到过几次,一般是因为没有设置UserAgnet,是网站屏蔽爬虫抓取的一种方式。通过FireBug可以从headers中获取到User-Agent信息: 获取到header信息后再调整下我们的代码,这次会需要使用一个新的类Request: 修改后依然在报错: 请求超时了,估计是因为网站在境外的缘故。所以还需要设置一个请求超时时间,只需要添加一个参数: 这样调整后终于请求成功了。需要强调下,这里的超时设置的时间单位是秒。 总结下吧,这次一共遇到了三个问题: 中文编码的问题; HTTP403错误的问题; 请求超时时间设置的问题。 完整的代码在这里,稍稍作了些调整,还添加了post请求的代码。在pot请求的代码中对字典型的参数调用了urlencode方法: 就是这样。这次本来是想说些关于网页解析的内容的,不过后来发现还是有很多的内容需要先说明下才好进行下去。关于网页解析的内容就挪到了下一节。 这里有篇文章不错,说明了urllib中几个常见的问题:http://www.cnblogs.com/ifso/p/4707135.html ##########
[阅读更多...] -
Python网络爬虫1 – 简单的Http请求
最近这段时间会有需要写一个网络爬虫。会在这里将实现网络爬虫的经验记录下来。 爬虫什么的,只是一个名字罢了。简单地说,也都是从http请求开始的。 Python实现http请求主要依赖的是urllib.request模块。例如发送http get请求: 就是这么简单。 通常,在命令行打印出来的是网页的源代码。想从中过滤出来需要的信息需要进行匹配和筛选。比如使用正则式匹配获取title和body中的内容: 对于一些采集程序来说做到这里就够了。如果我们要的是网页的内容而非网页的html,则需要使用比正则表达式更强大的工具。在下一节会用一个实例介绍相关的内容。 附上完整的程序: #########
[阅读更多...]