跳转到主要内容
ZetaChow 提交于 8 August 2013

drupal搭建网站很方便灵活,但是在搭建好网站之后需要做的就时填充内容了。

填充内容有人工和自动两种方式,blog、sns等应用靠网站主和网站用户自行添加内容,丰富网站,但其他一些应用靠人工就不太可能实现了,例如信息采集、数据同步,人工耗时耗力。

解决这个问题的思路有三种,其一,直接写入到drupal的数据库里,但是drupal的数据库设计挺复杂的,特别是一些字段的处理上,稍有疏忽会导致各种报错。

其二,http post方式,通过post方式去请求,但是问题是如果数据同步行为比较复杂,提交数据时还需要判断和已连串的操作的话,靠post后响应的html解析判断就很复杂了,甚至有的内容类型并没有提供写入的页面路径,这个方式就不是那么通用。

其三,使用RESTful,这是我现在使用的方法,需要安装额外的模块 Services3。

Services提供了标准的REST请求方式,响应数据有多种格式选择,其中最常用的就是json和xml。

安装好Services后,通过几步简单的设置就可以使用了。推荐再安装一个 Services Views 模块,这个模块可以通过view添加自定义的REST,其中可以通过设置view的字段、过滤条件、关联等实现很多高级的REST资源。

Services的相关内容可以在官网中查阅。

准备好Services后,接下来就是调用他并写入数据了。

各种语言中我选择了python,语法简单,效率也不错,而且我的需求是从另一个接口自动提取并写入drupal,所以不需要界面,也不需要用户触发,只需要在服务器上定时运行即可,因此只需要用python创建已个shell。

在python中http请求需要用到urllib2模块,传递参数时需要urlencode,因此还需要urllib模块,保持会话状态需要cookies,因此需要cookielib,解析响应的json需要用到json模块,因此在python脚本第一行导入这4个模块

import cookielib, urllib, urllib2,json

通过REST向drupal写入数据是需要登陆的,所以需要用到cookiejar,并且使用这个对象创建一个opener

#创建一个cookie处理对象
cookiejar = cookielib.CookieJar()
#使用build_opener创建urlOpener,使用刚才创建的cookie处理对象作为cookies处理对象
urlOpener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cookiejar))

接下来就时执行请求了,因为从登陆到查询到写入需要请求多次,我将没一次请求都需要写的代码封装成一个函数。

def goRequest(url='',values={},method='GET'):
    data = urllib.urlencode(values)

    request = urllib2.Request(baseURL%(url),data)
    
    #获取csrf,services3模块在写入和更新时需要验证
    if CSRF!=None:request.add_header("X-CSRF-Token", CSRF)
    request.get_method = lambda:method
    url=urlOpener.open(request)
    result = url.read()
    return result.decode('gbk').encode('utf-8')

 

封装了这个函数,身下的事情就很好办了

1.登陆

print goRequest(loginURL,{'username':'登陆名','password':'密码'},'POST')

(每一句执行我都使用print打印,为了调试方便,实际执行不需要print,直接调用就行了)

2.获取CSRF验证信息,之前的services模块不需要,但是目前的版本需要,验证方法就是在header里加入X-CSRF-Token描述,在封装的goRequest里有具体写法,在这里我只需要创建一个CSRF全局变量,然后获取CSRF的值,然后每次调用goRequest的时候CSRF就自动设置到header里了。

CSRF=goRequest(sessionTokenURL)

print CSRF #打印出来看看确保获取到数据

3.登陆完成,CSRF验证完成 ,接下来想做什么就做什么了。我在这里是请求写入content。

    print 'nodeInfo',nodeInfo
    try:
        #add new node
        goRequest('syn_res/node/‘
                  ,{"field_title":nodeInfo['title']
                    ,"field_name[und][0][value]":nodeInfo['name']}
                 ,'POST')
    except Exception,e:
        print e

这里用的是POST,services REST数据时可以使用GET、POST、PUT、DELETE,可以分别理解为查、增、改、删。对应操作选择不同的Method方式。

在以上代码中,nodeInfo是我从另一个位置查询并传递过来的json对象,测试时,可以先手工创建这个nodeInfo的json对象。

全部代码是这样的

#encoding:UTF-8
'''
Created on 2013-7-18

@author: ZetaChoww
'''
import cookielib, urllib, urllib2,json


#配置区域#################################################################
baseURL="http://localhost/drupal/?q=%s"
#########################################################################
loginURL ='res/user/login'  
sessionTokenURL ='services/session/token'



CSRF=None
##########################################################################

#创建一个cookie处理对象
cookiejar = cookielib.CookieJar()
#使用build_opener创建urlOpener,使用刚才创建的cookie处理对象作为cookies处理对象
urlOpener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cookiejar))


##########################################################################

def goRequest(url='',values={},method='GET'):
    data = urllib.urlencode(values)
    request = urllib2.Request(baseURL%(url),data)
    #获取csrf,services3模块在写入和更新时需要验证
    if CSRF!=None:request.add_header("X-CSRF-Token", CSRF)
    request.get_method = lambda:method
    url=urlOpener.open(request)
    result = url.read()
    return result.decode('gbk').encode('utf-8')


def sync_node(nodeinfo=None):
    #获取
    print 'nodeinfo',nodeinfo
    try:
        #modify some field from license info on Gzjjzd database
        goRequest('syn_res/node/"
                  ,{"title":nodeinfo[title]
                    ,"field_name[und][0][value]":nodeinfo['name']}
                 ,'POST')
    except Exception,e:
        print e
    
     
if __name__=='__main__':
    print '#Start login..'
    print goRequest(loginURL,{'username':'zz','password':'zzzzzz'},'POST')
    print '#Login OK!'
    CSRF=goRequest(sessionTokenURL)
    print '#CSRF:',CSRF
    print '#Start run'
    
    node={'title':'Hello!','name':'ZetaChow'}
    sync_node(node)
        
    #close the request opener
    urlOpener.close()

代码中删减了一些实际项目中的代码,所以可能有错漏,分享一下方法,代码不重要,用任何语言都可以实现类似功能,通过services模块,将内外数据整合在一起使得drupal可以用于更广阔的范围,drupal的entity的设计完全就时数据模型,services提供了一个途径在外部操作drupal的数据模型,这个方式也让团队其他成员能够加入到drupal项目的开发。

Drupal 版本