【极客学院】-python学习笔记-4-单线程爬虫 (提交表单抓取信息,实战练习)

本文介绍了如何使用Python进行单线程爬虫,包括通过GET和POST方法与网页交互,分析目标网站的表单提交功能,以及在实战中抓取极客学院前20页课程的名称、介绍、时间、等级和学习人数。在实践中遇到的问题和解决方案,如元素改变、正则表达式错误等也进行了详细说明。

极客学院课程地址:http://www.jikexueyuan.com/course/821_3.html?ss=1


向网页提交数据:


get,post 交互方式

分析目标网站

表单提交功能


get:从服务器获得数据

post:向服务器传数据


eg:

https://www.crowdfunder.com/browse/deals


request.post

步骤:构造表单,提交,获取返回信息


异步加载:菜做好一道上一道

不同于 菜做好了,再一次性上齐


网页 → 审查  → Headers  →  Request Method:GET (页面并没有 more,这里也没有POST)

Form Data → page:2


当有多个 page 的时候,直接提取,可能因为异步加载,无法提取到 more 的信息

这时 可以建立一个字典,并用 request.post 去提交表单,提取数据

#-*-coding:utf8-*-
import requests
import re

url='https://www.crowdfunder.com/'

data = {
    'entities_only':'true'<span style="background-color: rgb(153, 255, 153);">,</span>
    'page':'1'
}

html_post = requests.post(url<span style="background-color: rgb(153, 255, 153);">,</span>data=data)
title = re.findall('"card-title">(.*?)</div>', html_post.text, re.S)

for each in title:
    print (each)


继续

实战:

http://www.jikexueyuan.com/course/

前20页的 课程名称,课程介绍,课程时间,课程等级,学习人数


步骤:

requests获取网页

re.sub换页

正则表达式匹配内容


问题:

课程介绍,后面是省略号 的情况该怎么办?

爬虫只能爬到可以看到的

有的课程的课程介绍也可能为空


解决方案:

先抓大后抓小:


另:windows下将命令提示符的编码强制转化成utf-8

import  sys
reload(sys)
sys.setdefaultencoding("utf-8")



实践中遇到问题:

按课程中的代码敲出来后,会有一些错误


比如运行后,info.txt 中没有内容

因为:网页的元素不是课程里的那样了,所以需要修改匹配的线索,这样修改了 everyclass,title 和 content,


<li id="2430" test="0" deg="0" >


但是到 timeandlevel 又有错误了

TypeError: '_sre.SRE_Match' object is not subscriptable

说明:

“XXX”object is not subscriptable
说的是XXX对象不是可索引的,可索引的对象有list,tuple等
如果你确定你的XXX是一个可迭代对象的话,可以尝试用list()函数把它转化为列表,然后通过索引读取元素


尝试分部找规律,提出 time 和 level,

info['classtime'] = re.search('"time-icon"></i><em>(.*?)</em>',eachclass,re.S).group(1)
info['classlevel'] = re.search('"xinhao-<span style="color:#ff0000;">icon</span>"></i><em>(.*?)</em>',eachclass,re.S).group(1)
       

但是 有的 level 的 icon 后面可能会加数字,导致,查找出的结果会有none

<i class="xinhao-icon2"></i><em>中级</em>



后来发现问题在于,把 re.findall 写成了 re.search

timeandlevel = re.search('<em>(.*?)</em>',eachclass,re.S)


所以,只查找了一个 em,没办法分配哦

<_sre.SRE_Match object; span=(1122, 1146), match='<em>3课时\n\t\t\t\t\t\t\t57分钟</em>'>


而实际上是这样的数据
<dd class="mar-b8"><i class="time-icon"></i><em>4课时
<span style="white-space:pre">	</span>56分钟</em>
</dd>
<dd class="zhongji">
						
<span style="white-space:pre">	</span><i class="xinhao-icon"></i><em>初级</em>
</dd>


最终代码:

#-*-coding:utf8-*-

import requests
import re

#url = 'http://www.jikexueyuan.com/course/'

#html = requests.get(url)

#print(html.text)

#content = re.findall('', html.text, re.S)


class spider(object):
    def __init__(self):
        print (u'开始爬取内容...')

    def changepage(self,url,total_page):

        page_group = []                 #获得需要爬取的所有 page 的列表

        now_page = int(re.search('pageNum=(\d+)', url, re.S).group(1))

        for i in range(now_page,total_page+1):
            link = re.sub('pageNum=\d+','pageNum=%s'%i,url, re.S)
            page_group.append(link)

        return page_group

    def getsource(self,url):

        html = requests.get(url)        #获取网页源代码

        return html.text

    def geteveryclass(self,source):

        #everyclass = re.findall('(<li deg="".*?</li>)',source,re.S )        #先把所有课程找出来
        everyclass = re.findall('(<li id=.*?</li>)',source,re.S )

        return everyclass

    def getinfo(self,eachclass):

        info = {}                       #定义字典

        # info['title'] = re.search('target="_blank">(.*?)</a>',eachclass,re.S).group(1)
        info['title'] = re.search('title="(.*?)" alt',eachclass,re.S).group(1)
        # info['content'] = re.search('</h2><p>(.*?)</p>',eachclass,re.S).group(1)
        info['content'] = re.search('none;">(.*?)</p>',eachclass,re.S).group(1)
        #timeandlevel = re.search('<em>(.*?)</em>',eachclass,re.S)
        timeandlevel = re.<span style="background-color: rgb(153, 255, 153);">findall</span>('<em>(.*?)</em>',eachclass,re.S)
        info['classtime'] = timeandlevel[0]
        info['classlevel'] = timeandlevel[1]
        # info['classtime'] = re.search('"time-icon"></i><em>(.*?)</em>',eachclass,re.S).group(1)
        # info['classlevel'] = re.search('"xinhao-icon"></i><em>(.*?)</em>',eachclass,re.S).group(1)
        info['learnnum'] = re.search('"learn-number">(.*?)</em>',eachclass,re.S).group(1)

        return info

    def saveinfo(self,classinfo):

        f = open('info.txt','a')            #打开 info.txt, 使用追加的方式

        for each in classinfo:
            f.writelines('title:' + each['title'] + '\n')
            f.writelines('content:' + each['content'] + '\n')
            f.writelines('classtime:' + each['classtime'] + '\n')
            f.writelines('classlevel:' + each['classlevel'] + '\n')
            f.writelines('learnnum:' + each['learnnum'] + '\n\n')

        f.close()


#程序入口

if __name__ == '__main__':          #如果程序是自己使用 .py 文件运行

    classinfo = []                  #定义一个列表用来存课程信息

    url = 'http://www.jikexueyuan.com/course/?pageNum=1'

    jikespider = spider()           #实例化 类
    all_links = jikespider.changepage(url,2)

    for link in all_links:
        print(u'正在处理页面: '+ link)

        html = jikespider.getsource(link)

        everyclass = jikespider.geteveryclass(html)

        for each in everyclass:
            info = jikespider.getinfo(each)
            classinfo.append(info)

    jikespider.saveinfo(classinfo)


知识点:

参考:

Python中re(正则表达式)模块学习  http://www.cnblogs.com/sevenyuan/archive/2010/12/06/1898075.html


[1]re.search

  re.search函数会在字符串内查找模式匹配,只到找到第一个匹配然后返回,如果字符串没有匹配,则返回None。

re.search的函数原型为: re.search(pattern, string, flags)

每个参数的含意与re.match一样。 

re.match与re.search的区别:

re.match只匹配字符串的开始,如果字符串开始不符合正则表达式,则匹配失败,函数返回None;而re.search匹配整个字符串,直到找到一个匹配。

now_page = int(re.search('pageNum=(\d+)', url, re.S).group(1))


python group()

参考:http://www.cnblogs.com/kaituorensheng/archive/2012/08/20/2648209.html

正则表达式中,group()用来提出分组截获的字符串,()用来分组

例:

import re
a = "123abc456"
print re.search("([0-9]*)([a-z]*)([0-9]*)",a).group(0)   #123abc456,返回整体
print re.search("([0-9]*)([a-z]*)([0-9]*)",a).group(1)   #123
print re.search("([0-9]*)([a-z]*)([0-9]*)",a).group(2)   #abc
print re.search("([0-9]*)([a-z]*)([0-9]*)",a).group(3)   #456

re.search('pageNum=(\d+)', url, re.S)    <span style="white-space:pre">		</span>#<_sre.SRE_Match object; span=(35, 44), match='pageNum=1'>
re.search('pageNum=(\d+)', url, re.S).group(1)<span style="white-space:pre">		</span># 1

re.match

  re.match 尝试从字符串的开始匹配一个模式,如:下面的例子匹配第一个单词。 

re.match的函数原型为:re.match(pattern, string, flags)

第一个参数是正则表达式,这里为"(\w+)\s",如果匹配成功,则返回一个Match,否则返回一个None;

第二个参数表示要匹配的字符串;

第三个参数是标致位,用于控制正则表达式的匹配方式,如:是否区分大小写,多行匹配等等。


[2]re.sub

  re.sub用于替换字符串中的匹配项。下面一个例子将字符串中的空格 ' ' 替换成 '-' :  

re.sub的函数原型为:re.sub(pattern, repl, string, count)

其中第二个参数是替换后的字符串;本例中为'-'

第四个参数指替换个数。默认为0,表示每个匹配项都替换。

re.sub还允许使用函数对匹配项的替换进行复杂的处理。如:re.sub(r'\s', lambda m: '[' + m.group(0) + ']', text, 0);将字符串中的空格' '替换为'[ ]'。

for i in range(1,3):
    link = re.sub('pageNum=\d+','pageNum=%s'%i,url, re.S)
    print(link)
结果:
http://www.jikexueyuan.com/course/?pageNum=1
http://www.jikexueyuan.com/course/?pageNum=2


[3]re.findall

  re.findall可以获取字符串中所有匹配的字符串。如:re.findall(r'\w*oo\w*', text);获取字符串中,包含'oo'的所有单词。


everyclass = re.findall('(<li id=.*?</li>)',source,re.S)

timeandlevel = re.findall('<em>(.*?)</em>',eachclass,re.S)
info['classtime'] = timeandlevel[0]
info['classlevel'] = timeandlevel[1]



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值