利用Python实现简单的爬虫

490 阅读8分钟

爬虫原理

网络连接需要计算机一次Request请求和服务器端的Response回应。爬虫也需要做两件事:

  • 模拟计算机对服务器发起Request请求
  • 接收服务器端的Response内容并解析、提取所需要的信息。

Python第三方库的安装

在PyCharm中安装

  • 打开PyCharm,在菜单栏中选择File|Default Settings 命令
  • 选择左侧的 Project Interpreter选项,在窗口右侧选择Python环境

  • 单击右侧窗口左下角的➕号添加第三方库
  • 输入三方库名称,选中需要下载的库

  • 单击 Install Package 按钮安装(注:Windows平台下需要勾选 Install to user sit 复选框)。

在PIP中安装

安装Python之后,PIP会同时进行安装,验证PIP是否安装成功

pip3 --version

安装Python三方库

pip3 install packagename

注:如果使用Python2,pip3改为 pip 使用

手动安装

pip3 install wheel
  • cd到所下载库在本地的位置执行命令行
pip3 install packagename.whl

推荐方法为 PIP安装本地手动安装 方式。

第一个简单的爬虫

利用 request获取网络数据,利用BeautifulSoup解析数据。获取的数据自如网站租房信息的房屋信息Title。

一段代码

import  requests
from  bs4 import BeautifulSoup #导入相应的库

headers = {
    'User-Agent':'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36'
}#模拟浏览器请求头
res = requests.get('http://www.ziroom.com/z/nl/-z3.html?qwd=%E4%BA%AE%E9%A9%AC%E6%A1%A5',headers = headers) #请求网页
# print(res.text)

soup = BeautifulSoup(res.text,'html.parser') # 解析数据

# print(soup.prettify())

# price = soup.select('#houseList > li:nth-child(2) > div.txt > h3')
names = soup.select('#houseList > li:nth-of-type(2) > div.txt > h3')
# print(name.getText())

# names = soup.select('#houseList > li > div.txt > h3 > a')
#
for name in names:
    print(name.getText())

代码分析:

  • 关于headers

加入请求头来伪装陈浏览器,以便更好的获取数据。User-Agent获取方式,打开开发者工具,刷新网页后即可获取

  • select方法
soup.select('div.item > a > h1') #括号中的内容可通过浏览器复制得到

(1)鼠标定位到需要提取数据的位置,右击选择“检查”命令

(2)在网页源码中右击所选元素,在弹出的菜单中选择“Copy selector”,可得到:

#houseList > li:nth-child(2) > div.txt > h3

注:

nth-child(2)中的2表示的是所取得是列表中的第二个数据。

这个在Python中运行会报错,需要将其改为nth-of-type(2)

如果需要获取整个列表的所有信息,只需将nth-of-type(2)去掉就可以了。像这样:

names = soup.select('#houseList > li > div.txt > h3 > a')
  • getText()方法

使用该方法获取文字信息。

使用前获取的数据是这样的

<h3><a class="t1" href="//www.ziroom.com/z/vr/61425675.html" target="_blank">豪宅 · 星源汇1居室-西</a></h3>

使用后获取的数据是这样的

豪宅 · 星源汇1居室-西
  • get('attr')

用来获取属性信息,此代码中暂未用到。

最后再来个稍微复杂的例子来说明更多使用小技巧,不解释了。

实现效果,获取酷狗Top500的歌曲信息

import requests
from bs4 import BeautifulSoup
import  time
headers = {
    'User-Agent':'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36'
}

def get_music(url):
    res = requests.get(url,headers=headers)
    # print(res.text)
    # soup = BeautifulSoup(res.text,'lxml')
    soup = BeautifulSoup(res.text, 'html.parser')
    # print(soup.prettify())

    ranks = soup.select('#rankWrap > div.pc_temp_songlist > ul > li > span.pc_temp_num')
    titles = soup.select('#rankWrap > div.pc_temp_songlist > ul > li > a')
    for rank , title in zip(ranks,titles): # 多个数据的处理方式
        data = {
            'rank' : rank.getText().strip(), # 字符串的处理方式,去除两侧的空格
            'title' : title.getText(),
        }
        print(data)

if __name__ == '__main__':
    urls = ['http://www.kugou.com/yy/rank/home/{}-8888.html?from=rank'.format(str(i)) for i in range(1,24)]
    i = 0
    for url in urls:
        i = i + 1
        print('第{}页'.format(i))
        get_music(url)
        time.sleep(2) #以防数据获取速度太快出现问题

使用正则表达式来解析数据

正则表达式常用字符

一般字符

  • "." 匹配任意单个字符。例如 a.b ,可以匹配为abc、aic、a&c等,但不包含换行符
  • "" 转义字符,把字符改变为原来的意思。
  • [...] 字符集,相当于括号中任选一个。例如a[bcd],可以匹配为 ab、ac、ad。

预定义字符

数量词

  • "*" 匹配前一个字符0或无限次
  • "+" 至少匹配前一个字符一次
  • "?" 匹配前一个字符0次或者1次
  • "{m}" 匹配前一个字符 m 次
  • "{m,n}"匹配前一个字符m至n词

边界匹配

re模块

re模块拥有Python语言拥有的所有的正则表达式功能。

search()函数

用于匹配并提取第一个符合规律的内容。

语法如下:

re.search(pattern,string,flags=0)
  • pattern 为匹配的正则表达式
  • string 要匹配的字符串
  • flags 标志位,用于控制正则表达式的匹配方式,如是否区分大小写,多行匹配等。

例子:

import re

a = 'one12twp2three33four'

infos = re.search('\d+',a)
print(infos) #  <_sre.SRE_Match object; span=(3, 5), match='12'>
print(infos.group()) # group方法获取信息  12

sub()函数

用于替换字符串中的匹配项。

语法如下:

re.sub(pattern,repl,string,count=0,flags=0)
  • pattern 匹配的正则表达式
  • repl 替换的字符串
  • string 要被查找替换的原始字符串
  • count 模式匹配后替换的最大次数,默认0表示替换所有的匹配
  • flags 标志位,控制正则表达式的匹配方式

例子:

import re

phone = '181-1324-1341'

newphone = re.sub('\D+','',phone)

print(newphone) #18113241341

findall()函数

匹配所有符合规律的内容,并以列表的形式返回结果。

例子:

import re

a = 'one12twp2three33four'

infos = re.findall('\d+',a)

print(infos) #['12', '2', '33']

infos = re.findall('\d',a)

print(infos)  #['1', '2', '2', '3', '3']

完整的示例

获取斗破苍穹小说全部内容并存到本地文件中

import  requests
import re
import time

f = open('/Users/fangjiayou/Desktop/Last/untitled001.txt','a+')

def get_info(url):
    req = requests.get(url)
    if req.status_code == 200:
        contents = re.findall('<p>(.*?)</p>',req.content.decode('utf-8'),re.S)
        for content in contents:
            f.write(content+'\n\n')
    else:
        pass

if __name__ == '__main__':
    urls = ['http://www.doupoxs.com/doupocangqiong/{}.html'.format(str(number)) for number in range(2,1625)]
    for url in urls:
        print(url)
        get_info(url)
    f.close() #关闭文件,防止内存问题

Lxml库的使用并存放数据到excel中

三方库 xlwt的使用

写入Excel中需要的三方库

pip3 install xlwt

使用方式

import  xlwt

book = xlwt.Workbook(encoding='utf-8') # 创建工作簿
sheet = book.add_sheet('Sheet1') # 创建工作表
sheet.write(0,0,'python') # 在相应的单元格写入数据
sheet.write(1,1,'love')
book.save('test.xls') # 保存到文件中

实现效果:

举例 获取起点中文网小说信息

包含了lxml解析方式的使用

先上代码

import  xlwt
import requests
import  time
from lxml import  etree

all_info_list = [] # 初始化列表,存入爬虫数据

def get_info(url): # 定义获取爬虫信息的函数
    req = requests.get(url)
    html = etree.HTML(req.text)
    infos = html.xpath('//ul[@class="all-img-list cf"]/li')# 定位大标签,一次循环
    for info in infos:
        title = info.xpath('div[2]/h4/a/text()')[0]
        author = info.xpath('div[2]/p[1]/a[1]/text()')[0]
        complete = info.xpath('div[2]/p[1]/span/text()')[0]
        introduce = info.xpath('div[2]/p[2]/text()')[0].strip()
        info_list = [title,author,complete,introduce]
        all_info_list.append(info_list)
        print(info_list)
    time.sleep(1) # 睡眠1s

if __name__ == '__main__':
    urls = ['https://www.qidian.com/all?orderId=&style=1&pageSize=20&siteid=1&pubflag=0&hiddenField=0&page={}'.format(str(number)) for number in range(0,2)]
    for url in  urls:
        get_info(url)

    header = ['书名','作者','结束','简介']
    book = xlwt.Workbook(encoding='utf-8') # 创建工作簿
    sheet = book.add_sheet('Sheet1') #创建工作表
    for h in  range(len(header)):
        sheet.write(0,h,header[h]) # 写入表头

    row = 1
    for list in  all_info_list:
        colum = 0
        for data in list:
            sheet.write(row,colum,data)
            colum = colum + 1
        row = row + 1

    book.save('起点小说.xls') #保存文件

解析JSON数据

Python中有解析JSON数据的标准库,使用方式:

import json

使用方法很简单,只需一段代码说明:

import json

jsonstring = '{"user_man":[{"name":"peter"},{"name":"xiaoming"}],'\
    '"user_woman":[{"name":"Anni"},{"name":"zhangsan"}]}'

jsondata = json.loads(jsonstring)
print(jsondata.get("user_man")) #[{'name': 'peter'}, {'name': 'xiaoming'}]
print(jsondata.get('user_woman')[0].get('name')) #Anni

如何爬取图片

使用URLretrieve模块

URLretrieve模块是URLlib.request中的一部分,用法:

urlretrieve(url,path)

使用代码示例:

path = '/Users/fang/Desktop/Last/'
urlstr = 'http://g.hiphotos.baidu.com/image/h%3D300/sign=fb8af6169d2397ddc9799e046983b216/0823dd54564e92584fbb491f9082d158cdbf4eb0.jpg'

urlretrieve(urlstr,path+urlstr[-10:])

通过写入文件来获取图片

path = '/Users/fangjiayou/Desktop/Last/'
urlstr = 'http://g.hiphotos.baidu.com/image/h%3D300/sign=fb8af6169d2397ddc9799e046983b216/0823dd54564e92584fbb491f9082d158cdbf4eb0.jpg'

data = requests.get(urlstr)
fp = open(path+urlstr[-10:],'wb')
fp.write(data.content)
fp.close()

解析JSON数据并获取图片

示例流程为:根据关键词搜索对应的图片,解析数据进行图片下载。

代码:

from bs4 import BeautifulSoup
import requests
import json

header = {
    'accept':'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8',
    'User-Agent':'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36'
}

searchPicURL = 'https://www.pexels.com/search/'
word = input('请输入要下载的图片:')

url = searchPicURL+word
print(url)

data = requests.get(url)
soup = BeautifulSoup(data.text,'lxml')
images = soup.select('body > div.page-wrap > div.l-container > div.photos > article > a.js-photo-link > img')

path = '/Users/fangjiayou/Desktop/Last/'

i = 1
for image in  images:
    print('开始写入第{}张'.format(i))
    src = image.get('src')
    data = requests.get(src)
    relativepath = path+src.split('?')[0][-10:]
    fp = open(relativepath,'wb')
    fp.write(data.content)
    fp.close()
    print('第{}张写入结束'.format(i))
    i = i + 1

多线程的使用

Python中多线程爬虫使用multiprocess库,

使用方法:

from multiprocess import Pool

def method(url):
    print(url)

pool = Pool(processes=4)
pool.map(method,['1','2','3'])# method 为需要运行的函数,后面为迭代参数

通过爬取糗事百科数据来说明使用多线程的效率提升,代码如下

import requests
import re
from multiprocess import Pool
import time
headers = {
    'User-Agent':'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36'
}

def re_scraper(url):
    req = requests.get(url,headers=headers)
    titles = re.findall('<h2>(.*?)</h2>',req.text,re.S)
    contents = re.findall('<div class="content">\n<span>(.*?)</span>',req.text,re.S)
    # for title,content in zip(titles,contents):
        # print('Title:'+title+'\nContent:'+content)

if __name__ == '__main__':
    urls = ['https://www.qiushibaike.com/text/page/{}/'.format(str(page)) for page in range(1,30)]
    print("开始串行爬虫")
    start1 = time.time()
    for url in urls:
        re_scraper(url)
    end1 = time.time()
    print('串行爬虫消耗时间:',end1-start1)

    print('两个线程开始')
    start2 = time.time()
    pool2 = Pool(processes=2)
    pool2.map(re_scraper,urls)
    end2 = time.time()
    print('两个线程消耗时间:',end2-start2)

    print('四个线程开始')
    start4 = time.time()
    pool4 = Pool(processes=4)
    pool4.map(re_scraper, urls)
    end4 = time.time()
    print('四个线程消耗时间:', end4 - start4)

结果如下:

开始串行爬虫
串行爬虫消耗时间: 5.8548901081085205
两个线程开始
两个线程消耗时间: 2.986906051635742
四个线程开始
四个线程消耗时间: 1.016632080078125

注:并不是线程越多效率越高,根据硬件设置线程数才是最好的选择

----------------------------持续更新---------------------------