在BeautifulSoup中跳过NoneType属性

0 投票
3 回答
2164 浏览
提问于 2025-04-17 12:37

我正在用beautifulsoup和python解析Google的XML数据,这个过程非常顺利。同时,我还在创建一个CSV文件并上传到Google文档,这也没问题。不过,当我遇到XML中某个空的文本属性时,解析器就会停止工作。现在这不是问题,因为所有属性都有数据,但如果第一次遇到没有数据的情况,程序就会出错。

这段代码:

import atom
import gdata.auth
import gdata.contacts
import gdata.contacts.client
import gdata.docs.service
import gdata.docs.data
from BeautifulSoup import BeautifulStoneSoup as Soup
import csv

email = 'admin@domain.com'
password = 'password'
domain = 'domain.com'

ms_client = gdata.docs.service.DocsService()
gd_client = gdata.contacts.client.ContactsClient(domain=domain)
gd_client.ClientLogin(email, password, 'profileFeedAPI')
ms_client.ClientLogin(email, password, 'peopleCSVupload')

profiles_feed = gd_client.GetProfilesFeed('https://www.google.com/m8/feeds/profiles/domain/domain.com/full?max-results=300')

soup = Soup(str(profiles_feed), selfClosingTags=['ns0:category','ns3:status', 'ns0:link','ns1:email'])

a = soup.findAll('ns0:entry')
f = open('C:\\people.csv', 'wb')

writer = csv.writer(f, quoting=csv.QUOTE_NONE, escapechar =' ')

for entry in a:
    writer.writerow([entry.find('ns1:familyname').text + ',' + entry.find('ns1:givenname').text + ',' + entry.find('ns1:fullname').text + ',' + entry.find('ns1:orgtitle').text + ',' + entry.find('ns1:orgdepartment').text + ',' + entry.find('ns1:orgname').text + ',' + entry.find('ns1:email',primary=True)['address']])

f.close()

ms = gdata.data.MediaSource(file_path="C:\\people.csv", content_type=gdata.docs.service.SUPPORTED_FILETYPES['CSV'])
csv_entry = ms_client.Upload(ms, "People File")

我知道我可以这样做:

for entry in a:
    if entry.find('ns1:orgtitle') != None:
        print entry.find('ns1:orgtitle').text
    elif entry.find('ns1:orgtitle') == None:
        print('')
    if entry.find('ns1:familyname') != None:
        print entry.find('ns1:familyname').text
    elif entry.find('ns1:familyname') == None:
        print('')
        etc...

但这样做太长了,我不知道怎么把数据集中到一行显示。任何帮助都非常感谢。

3 个回答

0

把获取值和打印的过程放到函数里其实很简单。

def find(entry, spec, default=None):
    value = entry.find(spec)
    return default if value is None else value.text

def findandprint(entry, spec, default=None, newline=True):
    value = find(entry, spec, default)
    if value is not None:    # if we still don't have a value even after
        print value,         # considering default, don't print anything
        if newline:
            print

这样你就可以直接:

for entry in a:
    findandprint(entry, 'ns1:orgtitle',   default="")
    findandprint(entry, 'ns1:familyname', default="")

如果你有很多属性,并且想要对它们都做相同的处理,那就可以遍历它们:

for entry in a:
    for attribute in ('ns1:orgtitle', 'ns1:familyname', ...):
        findandprint(entry, attribute, default="")
1

一开始我不明白你为什么觉得会出错……你没有提供一个“有问题”的数据片段。BeautifulSoup 会很乐意返回一个空字符串。

在你提到的“必须滚动到那里才能看到”那句话的最后,终于清楚了你在找一个属性,正如你在开头所说的。

entry.find('ns1:email',primary=True)['address']

空属性不会像空文本节点那样安静地返回(比如 entry.find('ns1:familyname').text)。

别担心,只需把 ['address'] 的写法换成 .get('address',''),这样如果没有值就会返回一个空字符串,而不是抛出一个 KeyError 错误。

6

你可以把查找的部分包裹起来,像这样:

def findnonempty(entry, arg):
    result = entry.find(arg):
    if result:
        return result.text
    else:
        return "" 

然后你可以选择一个接一个地进行7次调用,或者你可以使用map(),像这样:

tags = ['ns1:familyname', 'ns1:givenname', ... ] # your tags
s = map(lambda tag: findnonempty(entry, tag), tags)
"".join(s)

撰写回答