Skip to main content

Importing malformed XML: Import[... "XMLObject"] vs ImportString[..., "XML"]


It appears that Import[ url , "XMLObject" ] is more forgiving of improperly formatted XML than ImportString[ string, "XML"]. For example, Mathematica is happy to do this


popNamesXML = 
Import["http://uscode.house.gov/popularnames/popularnames.htm",
"XMLObject"];

but complains about this


popNames = 

URLFetch["http://uscode.house.gov/popularnames/popularnames.htm"];
xml = ImportString[popNames, "XML"];

enter image description here


and barks about utf8 encoding for this


ImportString[popNames, {"HTML", "XMLObject"}]

In the Trace of the first snippet it appears these options are being passed to XMLGetString:


{"NormalizeWhitespace"->True,"IncludeNamespaces"->Automatic,
"ValidateAgainstDTD"->Automatic,"IncludeEmbeddedObjects"->None,

"AllowRemoteDTDAccess"->True,"ReadDTD"->True,"IncludeDefaultedAttributes"->False,
"AllowUnrecognizedEntities"->Auomatic,"PreserveCDATASections"->False}

But passing these to ImportString or directly to XMLGetString still results in the same errors.


This answer may be of interest but does not seem to be the problem, though I could be wrong. I tinkered with the suggestion there without luck. Does anyone know if/how I can make ImportString behave nicely here?



Answer



I am using Mathematica 8.0.4 and have no URLFetch command, so I have used Wget to download this 5915505 bytes file:


wget http://uscode.house.gov/popularnames/popularnames.htm

After downloading I evaluated



popNamesXML = Import["popularnames.htm", "XMLObject"]

And got the XMLObject without any errors. Then I Imported this file as "Text" and tried to get the XMLObject using ImportString:


popNames = Import["popularnames.htm", "Text"];
ImportString[popNames, {"HTML", "XMLObject"}]

I got a couple if the $CharacterEncoding::utf8 errors on the last step, instead of the XMLObject I got the plaintext version of the page.


So it is clear at least that the problem is not in URLFetch but in ImportString itself. I think it is worth to report it to Wolfram Support.


One workaround is to Export the popNames into a file with extension .htm, then Import it:


Export["popNames.htm", popNames, "Text"]

popNamesXML = Import["popNames.htm", "XMLObject"]

It poduces the desired XMLObject.


Another workaround is to use CharacterEncoding -> "WindowsANSI" option:


popNamesXML2 = ImportString[popNames, {"XHTML", "XMLObject"}, CharacterEncoding -> "WindowsANSI"]

It produces identical XMLObject without errors:


popNamesXML2 === popNamesXML



True

So the problem is that ImportString by default incorrectly chooses the "UTF8" encoding instead of "WindowsANSI". It looks like a bug since it is not related to global $CharacterEncoding variable: setting


$CharacterEncoding = "WindowsANSI"

does not change anything.


On the Documentation page for the "HTML" format we read:



If the character encoding of the file is not specified in the HTML file, Import uses the encoding specified by CharacterEncoding. A complete list of possible encodings is given by $CharacterEncodings.




This statement seems to be misleading for two reasons:


1) The default value for CharacterEncoding is Automatic and it is not clear how ImportString should behave in the case when character encoding is not specified in the HTML file.


2) In our case the character encoding IS specified in the HTML file on the first and fourth lines AND IS ignored when we provide explicit value for the CharacterEncoding option:





Manually deleting the "encoding='UTF-8'" and "charset=UTF-8" from the file in a plain text editor does fix incorrect behavior: now we get identical XMLObject without errors:


popNamesXML === ImportString[Import["popularnames_fixed.htm", "Text"], {"XHTML", "XMLObject"}]



True

I have also checked the server response (which should also contain the encoding information) using the -S switch of Wget:


wget -S http://uscode.house.gov/popularnames/popularnames.htm

and got



Content-Type: text/html;charset=UTF-8



It means Import gets no correct information about encoding from the server but is able to determine the correct encoding in this case.



So the final diagnosis is similar to what mfvonh suggested: Import can automatically handle the cases when incorrect encoding is specified in an HTML file while ImportString in such cases needs the correct encoding specified through the CharacterEncoding option.


Comments

Popular posts from this blog

functions - Get leading series expansion term?

Given a function f[x] , I would like to have a function leadingSeries that returns just the leading term in the series around x=0 . For example: leadingSeries[(1/x + 2)/(4 + 1/x^2 + x)] x and leadingSeries[(1/x + 2 + (1 - 1/x^3)/4)/(4 + x)] -(1/(16 x^3)) Is there such a function in Mathematica? Or maybe one can implement it efficiently? EDIT I finally went with the following implementation, based on Carl Woll 's answer: lds[ex_,x_]:=( (ex/.x->(x+O[x]^2))/.SeriesData[U_,Z_,L_List,Mi_,Ma_,De_]:>SeriesData[U,Z,{L[[1]]},Mi,Mi+1,De]//Quiet//Normal) The advantage is, that this one also properly works with functions whose leading term is a constant: lds[Exp[x],x] 1 Answer Update 1 Updated to eliminate SeriesData and to not return additional terms Perhaps you could use: leadingSeries[expr_, x_] := Normal[expr /. x->(x+O[x]^2) /. a_List :> Take[a, 1]] Then for your examples: leadingSeries[(1/x + 2)/(4 + 1/x^2 + x), x] leadingSeries[Exp[x], x] leadingSeries[(1/x + 2 + (1 - 1/x...

How to thread a list

I have data in format data = {{a1, a2}, {b1, b2}, {c1, c2}, {d1, d2}} Tableform: I want to thread it to : tdata = {{{a1, b1}, {a2, b2}}, {{a1, c1}, {a2, c2}}, {{a1, d1}, {a2, d2}}} Tableform: And I would like to do better then pseudofunction[n_] := Transpose[{data2[[1]], data2[[n]]}]; SetAttributes[pseudofunction, Listable]; Range[2, 4] // pseudofunction Here is my benchmark data, where data3 is normal sample of real data. data3 = Drop[ExcelWorkBook[[Column1 ;; Column4]], None, 1]; data2 = {a #, b #, c #, d #} & /@ Range[1, 10^5]; data = RandomReal[{0, 1}, {10^6, 4}]; Here is my benchmark code kptnw[list_] := Transpose[{Table[First@#, {Length@# - 1}], Rest@#}, {3, 1, 2}] &@list kptnw2[list_] := Transpose[{ConstantArray[First@#, Length@# - 1], Rest@#}, {3, 1, 2}] &@list OleksandrR[list_] := Flatten[Outer[List, List@First[list], Rest[list], 1], {{2}, {1, 4}}] paradox2[list_] := Partition[Riffle[list[[1]], #], 2] & /@ Drop[list, 1] RM[list_] := FoldList[Transpose[{First@li...

front end - keyboard shortcut to invoke Insert new matrix

I frequently need to type in some matrices, and the menu command Insert > Table/Matrix > New... allows matrices with lines drawn between columns and rows, which is very helpful. I would like to make a keyboard shortcut for it, but cannot find the relevant frontend token command (4209405) for it. Since the FullForm[] and InputForm[] of matrices with lines drawn between rows and columns is the same as those without lines, it's hard to do this via 3rd party system-wide text expanders (e.g. autohotkey or atext on mac). How does one assign a keyboard shortcut for the menu item Insert > Table/Matrix > New... , preferably using only mathematica? Thanks! Answer In the MenuSetup.tr (for linux located in the $InstallationDirectory/SystemFiles/FrontEnd/TextResources/X/ directory), I changed the line MenuItem["&New...", "CreateGridBoxDialog"] to read MenuItem["&New...", "CreateGridBoxDialog", MenuKey["m", Modifiers-...