Biz bazı firmalardan XML alamayınca siteyi crawl ediyoruz. Bunun içinde scrapy kullanıyoruz. Scrapy kullanırken bozuk XML lerde dahi çalıştığını farkettim. O zaman kaynak kodu inceledim.
https://github.com/scrapy/scrapy/blob/master/scrapy/utils/iterators.py#L18
def xmliter(obj, nodename):
"""Return a iterator of Selector's over all nodes of a XML document,
given the name of the node to iterate. Useful for parsing XML feeds.
obj can be:
- a Response object
- a unicode string
- a string encoded as utf-8
"""
nodename_patt = re.escape(nodename)
HEADER_START_RE = re.compile(r'^(.*?)<\s*%s(?:\s|>)' % nodename_patt, re.S)
HEADER_END_RE = re.compile(r'<\s*/%s\s*>' % nodename_patt, re.S)
text = _body_or_str(obj)
header_start = re.search(HEADER_START_RE, text)
header_start = header_start.group(1).strip() if header_start else ''
header_end = re_rsearch(HEADER_END_RE, text)
header_end = text[header_end[1]:].strip() if header_end else ''
r = re.compile(r'<%(np)s[\s>].*?</%(np)s>' % {'np': nodename_patt}, re.DOTALL)
for match in r.finditer(text):
nodetext = header_start + match.group() + header_end
yield Selector(text=nodetext, type='xml').xpath('//' + nodename)[0]
Yukarıda gördüğünüz üzere yapılan iş aslında çok basit.
Önce xml'i küçük parçalara ayıyor. Sonra her birini bağımsız birer xml elemanı gibi kontrol ediyor.
Mesela aşağıdaki gibi bir XML yapımız olsun. Dikkat ederseniz ID 5 olan kısımda etiketi bozuk oluşturdum. Büyük bir XML dosyamız olsa küçücük bir kısım yüzünden bütün XML çöp oluyor.
Kodları gist'e koydum. Buradan inceleyebilirsiniz.
https://gist.github.com/volkan/0d3af41be13a628201b1cee82246d095
$xml = '
';
echo fixXML($xml, 'product');
function fixXML($xml, $nodeName) {
$patternTag = sprintf('/<%s[\s>].*?<\/%s>/s', $nodeName, $nodeName);
preg_match_all($patternTag, $xml, $matches);
$start = '';
$end = ' ';
$list = [];
$wrongItem = [];
$doc = new \DOMDocument();
foreach ($matches[0] as $key => $item) {
try {
$tmpXML = $start . $item . $end;
$status = @$doc->loadXML($tmpXML);
if ($status == false) {
throw new \Exception;
}
$list[] = $item;
} catch (\Exception $e) {
$wrongItem[] = $item;
}
}
$xml = $start . implode("\n", $list) . $end;
return $xml;
}
Hiç yorum yok:
Yorum Gönder