用nodejs和python实现一个爬虫来爬智联招聘的信息

#####第一步:用python进行一个http请求
http.py

# coding: utf-8
import sys
import types
import urllib
import urllib2
# 获取传递的参数
# @param argv[0]{string}:脚本名称
# @param argv[1]{string}:请求方式,get或post
# @param argv[2]{string}:请求地址
# @param argv[3]{string}:请求的数据
    argv = {
    ‘filename’: sys.argv[0],
    ‘method’: sys.argv[1],
    ‘url’: sys.argv[2],
    ‘data’: sys.argv[3],
    }
class Http:
    # 初始化数据
    def __init__(self, method, url, data = ”):
    self.method = method # 请求的类型
    self.url = url # 请求的地址
    self.data = self.getData(data) # 请求的数据
    # 请求头
    self.header = {
    ‘Accept-Encoding’: ‘deflate’,
    ‘User-Agent’: ‘Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.143
    Safari/537.36′,
    ‘cache-control’: ‘no-cache’,
    }
    # 获取请求数据的
    def getData(self, data):
    if type(data) is types.StringType:
    gd = data
    elif type(data) is types.DictionaryType:
    gd = urllib.urlencode(data)
    else:
    gd = ”
    return gd
    # get
    def get(self):
    if self.data == ”:
    u = self.url
    else:
    u = self.url + ‘?’ + self.data
    request = urllib2.Request(u)
    response = urllib2.urlopen(request)
    return response.read()
    # post
    def post(self):
    request = urllib2.Request(self.url, self.data, self.header)
    response = urllib2.urlopen(request)
    return response.read()
    # init
    def init(self):
    if self.method == ‘get’:
    self.result = self.get()
    elif self.method == ‘post’:
    self.result = self.post()
    else:
    self.result = ”
# 初始化请求
    http = Http(argv[‘method’], argv[‘url’], argv[‘data’])
    http.init()
    text = http.result
# 输出请求
    print(text)

在该脚本中,使用sys库获取命令行传递的各种参数,使用types库进行数据类型的判断,使用urllib库和urllib2库进行网页内容的抓取。传递的参数有请求的方式、请求的url地址、请求的数据。
初始化后,根据传递的请求方式决定执行get请求还是post请求,执行请求后将结果输出出来,传回nodejs程序中。[/p]

#####第二步:nodejs和python实现通信
pyhttp.js

/**
* 与python脚本通信,进行一个请求
* @param info{object}:与python脚本通信的配置
* @param callback{function}:通信完成后执行的事件,传递参数为返回的数据
*/
const childProcess = require(‘child_process’)
function pyhttp(info, callback) {
    /* 发送请求 */
    return new Promise((resolve, reject) => {
        // cmd
        const cps = childProcess.spawn(‘python’, [
            // avgs
            info.file,
            info.method,
            info.url,
            info.data,
        ])
        // 储存文本
        let txt = ”
        // 错误
        cps.stderr.on(‘data’, function(data) {
            reject(data)
        })
        // 获取数据
        cps.stdout.on(‘data’, function(data) {
            txt += data
        })
        // 获取完数据
        cps.on(‘exit’, function(code) {
            resolve(txt)
        })
    })
        .then(callback)
        .catch((error) => {
            console.log(error)
        })
}
module.exports = pyhttp

在nodejs脚本中执行其他脚本并返回执行结果,使用child_process模块,语法为child_process.spawn(command, [args], [options]),command是命令,args是参数。
在这里我遇到了一个小小的坑,我之前是用的child_process.exec(command, [options], callback),但是这个的返回值是有大小限制的,因为网站的源代码比较大,导致报错。用child_process.spawn(command, [args], [options])或者重新设置返回值大小可解决。
调用pyhttp.js需要传递两个参数,第一个参数是运行python脚本的命令配置,第二个参数是回调函数,,传递脚本的运行结果。

#####第三步:对源代码进行处理

deal.js

/**
* 处理数据
* @param dealText{string}:获取到的页面源代码
* @param ishref{boolean}:是否获取下一页的地址,默认为false,不获取
*/
const cheerio = require(‘cheerio’)
/* 提取冒号后面的文本 */
const mhtext = (text) => text.replace(/.+:/, ”)
function each($, ishref = false) {
    const a = []
    // 获取table
    const $table = $(‘#newlist_list_content_table’).children(‘table’)
    for (let i = 0, j = $table.length; i < j; i++) {
        const $this = $table.eq(i)
        const $tr = $this.children(‘tr’),
            $tr0 = $tr.eq(0),
            $tr1 = $tr.eq(1)
        const $span = $tr1
            .children(‘td’)
            .children(‘div’)
            .children(‘div’)
            .children(‘ul’)
            .children(‘li’)
            .children(‘span’)
        if ($this.children(‘tr’).children(‘th’).length <= 0) {
            a.push({
                // 职位招聘
                zwzp: $tr0
                    .children(‘.zwmc’)
                    .children(‘div’)
                    .children(‘a’)
                    .html(),
                // 招聘地址
                zpdz: $tr0
                    .children(‘.zwmc’)
                    .children(‘div’)
                    .children(‘a’)
                    .prop(‘href’),
                // 反馈率
                fklv: $tr0
                    .children(‘.fk_lv’)
                    .children(‘span’)
                    .html(),
                // 公司名称
                gsmc: $tr0
                    .children(‘.gsmc’)
                    .children(‘a’)
                    .html(),
                // 工作地点
                gzdd: $tr0.children(‘.gzdd’).html(),
                // 进入地址
                zldz: $tr0
                    .children(‘.gsmc’)
                    .children(‘a’)
                    .prop(‘href’),
                // 公司性质
                gsxz: mhtext($span.eq(1).html()),
                // 公司规模
                gsgm: mhtext($span.eq(2).html()),
            })
        }
    }
    const r = {}
    r[‘list’] = a
    if (ishref != false) {
        r[‘href’] = $(‘.pagesDown’)
            .children(‘ul’)
            .children(‘li’)
            .children(‘a’)
            .eq(2)
            .prop(‘href’)
            .replace(/&p=\d/, ”)
    }
    return r
}
function deal(dealText, ishref = false) {
    const $ = cheerio.load(dealText, {
        decodeEntities: false,
    })
    return each($, ishref)
}
module.exports = deal

deal.jscheerio模块来对抓取到的源代码进行处理。传递参数dealText为源代码,ishref 为是否抓取分页的地址。
注意,在用cheerio模块来获取数据时有一个问题,
注意

const cheerio = require(‘cheerio’)
const html = `<div id=”demo”>
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
</div>`
const $ = cheerio.load(html)
/* 获取li */
$(‘#demo’).children(‘li’) // 这样是获取不到li的
$(‘#demo’)
    .children(‘ul’)
    .children(‘li’) // 获取到了li

虽然cheerio的语法和jquery一样,但是原理千差万别,因为网页的数据被解析成了object对象,所以必须通过子节点一级一级向下查找,不能跳级。
数据处理:公司性质和公司规模删除掉了:和:前面的文字,下一页的url地址删除掉&p=\d参数,该参数是分页参数。
[/p]

[p]#####第四步:nodejs和python实现通信
app.js

const fs = require(‘fs’)
const pyhttp = require(‘./pyhttp’)
const deal = require(‘./deal’)
const _result = {}
/**
* 请求地址和参数
*
* jl:地点
* kw:职位关键字
* sf:工资范围下限
* st:工资范围上限
* el:学历
* et:职位类型
* pd:发布时间
* p: 分页page
* ct:公司性质
* sb:相关度
* we: 工作经验
*
*/
const info = (url, method = ‘get’, data = ”) => {
    return {
        // python脚本
        file: ‘http.py’,
        // 请求类型
        method: method,
        // 请求地址
        url: url,
        // 请求数据
        data: data,
    }
}
// 回调
const callback = (text) => {
    return new Promise((resolve, reject) => {
        resolve(text)
    })
}
pyhttp(
    info(
        encodeURI(
            ‘http://sou.zhaopin.com/jobs/searchresult.ashx?’ +
                ‘jl=北京&kw=web前端&sm=0&sf=10001&st=15000&el=4&we=0103&isfilter=1&p=1&et=2’,
        ),
    ),
    function(text) {
        const p0 = deal(text, true)
        _result.list = p0.list
        const n = []
        for (let i = 0; i < 4; i++) {
            n.push(pyhttp(info(`${p0.href}&p=${i + 2}`)), callback)
        }
        Promise.all(n)
            .then((result) => {
                for (let i in result) {
                    _result.list = _result.list.concat(deal(result[i]).list)
                }
            })
            .then(() => {
                fs.writeFile(
                    ‘./result/result.js’,
                    `window._result = ${JSON.stringify(_result, null, 4)};`,
                    (error) => {
                        if (error) {
                            console.log(error)
                        } else {
                            console.log(‘写入数据成功!’)
                        }
                    },
                )
            })
    },
)

pyhttp.jsdeal.js包含进来后,
首先对智联的搜索页进行一次请求,
回调函数内处理返回的源代码,将第一页数据添加到数组,并且获取到了分页的地址,
使用Promise.all并行请求第2页到第n页,
回调函数内对数据进行处理并添加到数组中,
将数据写入result.js里面(选择js而不是json是为了便于数据在html上展现)。

#####第五步:页面上展现数据
index.jsx

/* 渲染单个数据 */
const Group = React.createClass({
    // 处理a标签
    dela: (str) => str.replace(/<a.*>.*<\/a>/g, ”),
    // 处理多出来的标签
    delb: (str) => str.replace(/<\/?[^<>]>/g, ”),
    render: function() {
        return (
            <tr key={this.props.key}>
                <td>
                    <a href={this.props.obj.zpdz} target=”_blank”>
                        {this.delb(this.props.obj.zwzp)}
                    </a>
                </td>
                <td>{this.props.obj.fklv}</td>
                <td>{this.dela(this.props.obj.gsmc)}</td>
                <td>{this.props.obj.gzdd}</td>
                <td>
                    <a href={this.props.obj.zldz} target=”_blank”>
                        {decodeURI(this.props.obj.zldz)}
                    </a>
                </td>
                <td>{this.props.obj.gsxz}</td>
                <td>{this.props.obj.gsgm}</td>
            </tr>
        )
    },
})
/* 表格类 */
const Table = React.createClass({
    // 渲染组
    group: function() {
        const _this = this
        return window._result.list.map((object, index) => {
            return <Group key={index} obj={object} />
        })
    },
    render: function() {
        return (
            <table className=”table table-bordered table-hover table-condensed table-striped”>
                <thead>
                    <tr className=”info”>
                        <th className=”td0″>职位</th>
                        <th className=”td1″>反馈率</th>
                        <th className=”td2″>公司名称</th>
                        <th className=”td3″>工作地点</th>
                        <th className=”td4″>智联地址</th>
                        <th className=”td5″>公司性质</th>
                        <th className=”td6″>公司规模</th>
                    </tr>
                </thead>
                <tbody>{this.group()}</tbody>
            </table>
        )
    },
})
ReactDOM.render(<Table />, document.getElementById(‘result’))

在页面上展示数据,使用react和bootstrap。其中在展示时,公司名称发现有无用a标签,职位内有b标签,使用正则表达式删除它们。

web前端开发 模板
前端开发项目描述模板
前端开发ppt模板
赞(0)
前端开发者 » 用nodejs和python实现一个爬虫来爬智联招聘的信息
64K

评论 抢沙发

评论前必须登录!