Python 爬虫学习笔记之多线程爬虫_python

XPath 的安装以及使用

1 . XPath 的介绍

刚学过正则表达式,用的正顺手,现在就把正则表达式替换掉,使用 XPath,有人表示这太坑爹了,早知道刚上来就学习 XPath 多省事 啊。其实我个人认为学习一下正则表达式是大有益处的,之所以换成 XPath ,我个人认为是因为它定位更准确,使用更加便捷。可能有的人对 XPath 和正则表达式的区别不太清楚,举个例子来说吧,用正则表达式提取我们的内容,就好比说一个人想去天安门,地址的描述是左边有一个圆形建筑,右边是一个方形建筑,你去找吧,而使用 XPath 的话,地址的描述就变成了天安门的具体地址。怎么样?相比之下,哪种方式效率更高,找的更准确呢?

2 . XPath 的安装

XPath 包含在 lxml 库中,那么我们到哪里去下载呢? 点击此处 ,进入网页后按住 ctrl+f 搜索 lxml ,然后进行下载,下载完毕之后将文件拓展名改为 .zip ,然后进行解压,将名为 lxml 的文件夹复制粘贴到 Python 的 Lib 目录下,这样就安装完毕了。

3 . XPath 的使用

为了方便演示,我利用 Html 写了个简单的网页,代码如下所示(为了节省时间,方便小伙伴们直接进行测试,可直接复制粘贴我的代码)

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Test Html</title>
</head>
<body>
<div id="content">
  <ul id="like">
    <li>like one</li>
    <li>like two</li>
    <li>like three</li>
  </ul>

  <ul id="hate">
    <li>hate one</li>
    <li>hate two</li>
    <li>hate three</li>
  </ul>

  <div id="url">
    <a href="http://www.baidu.com">百度一下</a>
    <a href="http://www.hao123.com">好123</a>
  </div>
</div>

</body></html>

用谷歌浏览器打开这个网页,然后右击,选择检查,会出现如下所示界面

这个时候你鼠标右击任何一行 html 代码,都可以看到一个 Copy,将鼠标放上去,就可以看到 Copy XPath ,先复制下来,怎么用呢?

# coding=utf-8
from lxml import etree

f = open('myHtml.html','r')
html = f.read()
f.close()

selector = etree.HTML(html)
content = selector.xpath('//*[@id="like"]/li/text()')
for each in content:
  print each

看看打印结果

like one
like two
like three

很显然,将我们想要的内容打印下来了,注意我们在 xpath() 中使用了 text() 函数,这个函数就是获取其中的内容,但是如果我们想获取一个属性,该怎么办?比如说我们想得到 html 中的两个链接地址,也就是 href 属性,我们可以这么操作

content = selector.xpath('//*[@id="url"]/a/@href')
for each in content:
  print each

这个时候的打印结果就是

http://www.baidu.com
http://www.hao123.com

看到现在大家大概也就对 xpath() 中的符号有了一定的了解,比如一开始的 // 指的就是根目录,而 / 就是父节点下的子节点,其他的 id 属性也是一步一步从上往下寻找的,由于这是一种树结构,所以也难怪方法的名字为 etree()。

4 . XPath 的特殊用法

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>
<div id="likeone">like one</div>
<div id="liketwo">like two</div>
<div id="likethree">like three</div>

</body>
</html>

面对上面的一个网页,我们应该如何获取到三行的内容的 ? 嗯哼,很简单,我写三个 XPath 语句不就好了,so easy 。 如果真是这样,那么我们的效率好像是太低了一点,仔细看看这三行 div 的 id 属性,好像前四个字母都是 like, 那就好办了,我们可以使用 starts-with 对这三行进行同时提取,如下所示

content = selector.xpath('//div[starts-with(@id,"like")]/text()')

不过这样有一点麻烦的地方,我们就需要手动的去写 XPath 路径了,当然也可以复制粘贴下来在进行修改,这就是提升复杂度来换取效率的问题了。再来看看标签嵌套标签的提取情况

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>

<div id="content">
  <div id="text">
    <p>hello
      <b> world
        <font color="#ffe4c4">
          Python
        </font>
      </b>
    </p>
  </div>
</div>

</body>
</html>

像上面这样的一个网页,如果我们想获取到  hello world Python 语句,该怎么获取呢?很明显这是一种标签嵌套标签的情况,我们按照正常情况进行提取,看看结果如何

content = selector.xpath('//*[@id="text"]/p/text()')
for each in content:
  print each

运行之后,很遗憾的,只打印出了 hello 字样,其他字符丢失了,该怎么办呢?这种情况可以借助于 string(.)如下所示

content = selector.xpath('//*[@id="text"]/p')[0]
info = content.xpath('string(.)')
data = info.replace('\n','').replace(' ','')
print data

这样就可以打印出正确内容了,至于第三行为什么存在,你可以将其去掉看看结果,到时候你自然就明白了。

Python 并行化的简单介绍

有人说 Python 中的并行化并不是真正的并行化,但是多线程还是能够显著提高我们代码的执行效率,为我们节省下来一大笔时间,下面我们就针对单线程和多线程进行时间上的比较。

# coding=utf-8
import requests
from multiprocessing.dummy import Pool as ThreadPool
import time

def getsource(url):
  html = requests.get(url)

if __name__ == '__main__':
  urls = []
  for i in range(50, 500, 50):
    newpage = 'http://tieba.baidu.com/f?kw=python&ie=utf-8&pn=' + str(i)
    urls.append(newpage)

  # 单线程计时
  time1 = time.time()
  for i in urls:
    print i
    getsource(i)
  time2 = time.time()

  print '单线程耗时 : ' + str(time2 - time1) + ' s'

  # 多线程计时
  pool = ThreadPool(4)
  time3 = time.time()
  results = pool.map(getsource, urls)
  pool.close()
  pool.join()
  time4 = time.time()
  print '多线程耗时 : ' + str(time4 - time3) + ' s'

打印结果为

http://tieba.baidu.com/f?kw=python&ie=utf-8&pn=50
http://tieba.baidu.com/f?kw=python&ie=utf-8&pn=100
http://tieba.baidu.com/f?kw=python&ie=utf-8&pn=150
http://tieba.baidu.com/f?kw=python&ie=utf-8&pn=200
http://tieba.baidu.com/f?kw=python&ie=utf-8&pn=250
http://tieba.baidu.com/f?kw=python&ie=utf-8&pn=300
http://tieba.baidu.com/f?kw=python&ie=utf-8&pn=350
http://tieba.baidu.com/f?kw=python&ie=utf-8&pn=400
http://tieba.baidu.com/f?kw=python&ie=utf-8&pn=450
单线程耗时 : 7.26399993896 s
多线程耗时 : 2.49799990654 s

至于以上链接为什么设置间隔为 50,是因为我发现在百度贴吧上没翻一页,pn 的值就会增加 50。 通过以上结果我们发现,多线程相比于单线程效率提升了太多太多。至于以上代码中多线程的使用,我就不再过多讲解,我相信只要接触过 Java 的人对多线程的使用不会陌生,其实都是大差不差。没有接触过 Java ?那就对不起了,以上代码请自行消化吧。

实战 -- 爬取当当网书籍信息

一直以来都在当当网购买书籍,既然学会了如何利用 Python 爬取信息,那么首先就来爬取一下当当网中的书籍信息吧。本实战完成之后的内容如下所示

在当当网中搜索 Java ,出现了89页内容,我选择爬取了前 80 页,而且为了比较多线程和单线程的效率,我特意在这里对二者进行了比较,其中单线程爬取所用时间为 67s,而多线程仅为 15s 。

如何爬取网页,在上面 XPath 的使用中我们也已经做了介绍,无非就是进入网页,右击选择检查,查看网页 html 代码,然后寻找规律,进行信息的提取,在这里就不在多介绍,由于代码比较短,所以在这里直接上源代码。

# coding=utf8
import requests
import re
import time
from lxml import etree
from multiprocessing.dummy import Pool as ThreadPool
import sys

reload(sys)
sys.setdefaultencoding('utf-8')

def changepage(url, total):
  urls = []
  nowpage = int(re.search('(\d+)', url, re.S).group(1))
  for i in range(nowpage, total + 1):
    link = re.sub('page_index=(\d+)', 'page_index=%s' % i, url, re.S)
    urls.append(link)
  return urls

def spider(url):
  html = requests.get(url)
  content = html.text

  selector = etree.HTML(content)
  title = []
  title = selector.xpath('//*[@id="component_0__0__6612"]/li/a/@title')

  detail = []
  detail = selector.xpath('//*[@id="component_0__0__6612"]/li/p[3]/span[1]/text()')
  saveinfo(title,detail)

def saveinfo(title, detail):
  length1 = len(title)
  for i in range(0, length1 - 1):
    f.writelines(title[i] + '\n')
    f.writelines(detail[i] + '\n\n')

if __name__ == '__main__':
  pool = ThreadPool(4)
  f = open('info.txt', 'a')
  url = 'http://search.dangdang.com/?key=Java&act=input&page_index=1'
  urls = changepage(url, 80)

  time1 = time.time()
  pool.map(spider, urls)
  pool.close()
  pool.join()

  f.close()
  print '爬取成功!'
  time2 = time.time()
  print '多线程耗时 : ' + str(time2 - time1) + 's'

  # time1 = time.time()
  # for each in urls:
  #   spider(each)
  # time2 = time.time()
  # f.close()

  # print '单线程耗时 : ' + str(time2 - time1) + 's'

可见,以上代码中的知识,我们都在介绍 XPath 和 并行化 中做了详细的介绍,所以阅读起来十分轻松。

好了,到今天为止,Python 爬虫相关系列的文章到此结束,谢谢你的观看。

以上是小编为您精心准备的的内容,在的博客、问答、公众号、人物、课程等栏目也有的相关内容,欢迎继续使用右上角搜索按钮进行搜索python
, 爬虫
, 多线程
, xpath
python3
,以便于您获取更多的相关知识。

时间: 2024-12-30 05:13:09

Python 爬虫学习笔记之多线程爬虫_python的相关文章

Python 爬虫学习笔记之单线程爬虫_python

介绍 本篇文章主要介绍如何爬取麦子学院的课程信息(本爬虫仍是单线程爬虫),在开始介绍之前,先来看看结果示意图 怎么样,是不是已经跃跃欲试了?首先让我们打开麦子学院的网址,然后找到麦子学院的全部课程信息,像下面这样 这个时候进行翻页,观看网址的变化,首先,第一页的网址是 http://www.maiziedu.com/course/list/, 第二页变成了 http://www.maiziedu.com/course/list/all-all/0-2/, 第三页变成了 http://www.ma

php与python实现的线程池多线程爬虫功能示例_php技巧

本文实例讲述了php与python实现的线程池多线程爬虫功能.分享给大家供大家参考,具体如下: 多线程爬虫可以用于抓取内容了这个可以提升性能了,这里我们来看php与python 线程池多线程爬虫的例子,代码如下: php例子 <?php class Connect extends Worker //worker模式 { public function __construct() { } public function getConnection() { if (!self::$ch) { sel

python函数学习笔记

#!/usr/bin/env python #个人学习笔记,无其他用途 def add(x=9,y=10): if x>y: a=x+y print "x+y" return a if x<y: a=y-x print "y-x" return a if x==y: a=x*y print "x*y" return a print add(2,3) print add(5,4) print add(6,6) 将一个数组的值传递给函数

python基础学习笔记(九)

python异常   python用异常对象(exception object)来表示异常情况.遇到错误后,会引发异常.如果异常对象并未被处理或捕捉,程序就会用所谓的 回溯(Traceback, 一种错误信息)终止执行: >>> 1/0 Traceback (most recent call last): File "<pyshell#0>", line 1, in <module> 1/0 ZeroDivisionError: integer

python算法学习之计数排序实例_python

python算法学习之计数排序实例 复制代码 代码如下: # -*- coding: utf-8 -*- def _counting_sort(A, B, k):    """计数排序,伪码如下:    COUNTING-SORT(A, B, k)    1  for i ← 0 to k // 初始化存储区的值    2    do C[i] ← 0    3  for j ← 1 to length[A] // 为各值计数    4    do C[A[j]] ← C[A

python进阶学习笔记(四)--多线程thread

在使用多线程之前,我们首页要理解什么是进程和线程. 什么是进程? 计算机程序只不过是磁盘中可执行的,二进制(或其它类型) 的数据.它们只有在被读取到内存中,被操作系统调用的时候才开始它们的生命期.进程(有时被称为重量级进程)是程序的一次执行.每个进程都有自己的地址空 间,内存,数据栈以及其它记录其运行轨迹的辅助数据.操作系统管理在其上运行的所有进程,并为这些进程公平地分配时间. 什么是线程? 线程(有时被称为轻量级进程)跟进程有些相似,不同的是,所有的线程运行在同一个进程中,共享相同的运行环境.

Python异常学习笔记_python

异常(exceptions)是Python中一种非常重要的类型,它和语法错误不同,是在程序运行期间引发的错误.Python中内置了很多异常,如IOError,NameError,KeyboardInterrupt等,更多的异常可以点击这里. 异常的意义在于提供一种更加优雅的运行方式,例如用Python编写一个计算器,如果用户输入不能计算的对象,则可以抛出异常,并进行处理, 如下: while True: try: x= int(input('Please In enter A number:')

Python tempfile模块学习笔记(临时文件)_python

tempfile.TemporaryFile 如何你的应用程序需要一个临时文件来存储数据,但不需要同其他程序共享,那么用TemporaryFile函数创建临时文件是最好的选择.其他的应用程序是无法找到或打开这个文件的,因为它并没有引用文件系统表.用这个函数创建的临时文件,关闭后会自动删除. 实例一: 复制代码 代码如下: import osimport tempfile print 'Building a file name yourself:'filename = '/tmp/guess_my

Python函数学习笔记_python

局部名字静态检测 Python探测局部作用域的时候:是在python编译代码时检测,而不是通过他们在运行时的赋值. 正常的情况下,没在函数中复制的名字将在包含它的模块中查找: >>> x=99 >>> def selector(): ... print x ... >>> selector() 99 但是: >>> def selector(): ... print x ... x=100 ... >>> selec